2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.sample.toaster.provider;
10 import java.util.Arrays;
11 import java.util.Collections;
12 import java.util.concurrent.Callable;
13 import java.util.concurrent.ExecutionException;
14 import java.util.concurrent.ExecutorService;
15 import java.util.concurrent.Executors;
16 import java.util.concurrent.Future;
17 import java.util.concurrent.atomic.AtomicLong;
19 import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
22 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
26 import org.opendaylight.controller.sal.common.util.RpcErrors;
27 import org.opendaylight.controller.sal.common.util.Rpcs;
28 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.DisplayString;
29 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput;
30 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.RestockToasterInput;
31 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.Toaster;
32 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.Toaster.ToasterStatus;
33 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterBuilder;
34 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterOutOfBreadBuilder;
35 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestocked;
36 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestockedBuilder;
37 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
38 import org.opendaylight.yangtools.yang.binding.DataObject;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40 import org.opendaylight.yangtools.yang.common.RpcError;
41 import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
42 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
43 import org.opendaylight.yangtools.yang.common.RpcResult;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 import com.google.common.util.concurrent.Futures;
49 public class OpendaylightToaster implements ToasterService, ToasterProviderRuntimeMXBean,
50 DataChangeListener, AutoCloseable {
52 private static final Logger LOG = LoggerFactory.getLogger(OpendaylightToaster.class);
54 public static final InstanceIdentifier<Toaster> TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
56 private static final DisplayString TOASTER_MANUFACTURER = new DisplayString("Opendaylight");
57 private static final DisplayString TOASTER_MODEL_NUMBER = new DisplayString("Model 1 - Binding Aware");
59 private NotificationProviderService notificationProvider;
60 private DataBroker dataProvider;
62 private final ExecutorService executor;
64 // As you will see we are using multiple threads here. Therefore we need to be careful about concurrency.
65 // In this case we use the taskLock to provide synchronization for the current task.
66 private volatile Future<RpcResult<Void>> currentTask;
67 private final Object taskLock = new Object();
69 private final AtomicLong amountOfBreadInStock = new AtomicLong( 100 );
71 private final AtomicLong toastsMade = new AtomicLong(0);
73 // Thread safe holder for our darkness multiplier.
74 private final AtomicLong darknessFactor = new AtomicLong( 1000 );
76 public OpendaylightToaster() {
77 executor = Executors.newFixedThreadPool(1);
80 public void setNotificationProvider(final NotificationProviderService salService) {
81 this.notificationProvider = salService;
84 public void setDataProvider(final DataBroker salDataProvider) {
85 this.dataProvider = salDataProvider;
90 * Implemented from the AutoCloseable interface.
93 public void close() throws ExecutionException, InterruptedException {
94 // When we close this service we need to shutdown our executor!
97 if (dataProvider != null) {
98 WriteTransaction t = dataProvider.newWriteOnlyTransaction();
99 t.delete(LogicalDatastoreType.OPERATIONAL,TOASTER_IID);
100 t.commit().get(); // FIXME: This call should not be blocking.
104 private Toaster buildToaster() {
105 // We don't need to synchronize on currentTask here b/c it's declared volatile and
106 // we're just doing a read.
107 boolean isUp = currentTask == null;
109 // note - we are simulating a device whose manufacture and model are
110 // fixed (embedded) into the hardware.
111 // This is why the manufacture and model number are hardcoded.
112 ToasterBuilder tb = new ToasterBuilder();
113 tb.setToasterManufacturer(TOASTER_MANUFACTURER).setToasterModelNumber(TOASTER_MODEL_NUMBER)
114 .setToasterStatus(isUp ? ToasterStatus.Up : ToasterStatus.Down);
119 * Implemented from the DataChangeListener interface.
122 public void onDataChanged( final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change ) {
123 DataObject dataObject = change.getUpdatedSubtree();
124 if( dataObject instanceof Toaster )
126 Toaster toaster = (Toaster) dataObject;
127 Long darkness = toaster.getDarknessFactor();
128 if( darkness != null )
130 darknessFactor.set( darkness );
136 * RestConf RPC call implemented from the ToasterService interface.
139 public Future<RpcResult<Void>> cancelToast() {
140 synchronized (taskLock) {
141 if (currentTask != null) {
142 currentTask.cancel(true);
146 // Always return success from the cancel toast call.
147 return Futures.immediateFuture(Rpcs.<Void> getRpcResult(true, Collections.<RpcError> emptySet()));
151 * RestConf RPC call implemented from the ToasterService interface.
154 public Future<RpcResult<Void>> makeToast(final MakeToastInput input) {
155 LOG.info("makeToast: " + input);
157 synchronized (taskLock) {
158 if (currentTask != null) {
159 // return an error since we are already toasting some toast.
160 LOG.info( "Toaster is already making toast" );
162 RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Arrays.asList(
163 RpcErrors.getRpcError( "", "in-use", null, ErrorSeverity.WARNING,
164 "Toaster is busy", ErrorType.APPLICATION, null ) ) );
165 return Futures.immediateFuture(result);
167 else if( outOfBread() ) {
168 RpcResult<Void> result = Rpcs.<Void> getRpcResult(false, null, Arrays.asList(
169 RpcErrors.getRpcError( "out-of-stock", "resource-denied", null, null,
170 "Toaster is out of bread",
171 ErrorType.APPLICATION, null ) ) );
172 return Futures.immediateFuture(result);
175 // Notice that we are moving the actual call to another thread,
176 // allowing this thread to return immediately.
177 // The MD-SAL design encourages asynchronus programming. If the
178 // caller needs to block until the call is
179 // complete then they can leverage the blocking methods on the
181 currentTask = executor.submit(new MakeToastTask(input));
190 * RestConf RPC call implemented from the ToasterService interface.
191 * Restocks the bread for the toaster, resets the toastsMade counter to 0, and sends a
192 * ToasterRestocked notification.
195 public Future<RpcResult<java.lang.Void>> restockToaster(final RestockToasterInput input) {
196 LOG.info( "restockToaster: " + input );
198 synchronized( taskLock ) {
199 amountOfBreadInStock.set( input.getAmountOfBreadToStock() );
201 if( amountOfBreadInStock.get() > 0 ) {
202 ToasterRestocked reStockedNotification =
203 new ToasterRestockedBuilder().setAmountOfBread( input.getAmountOfBreadToStock() ).build();
204 notificationProvider.publish( reStockedNotification );
208 return Futures.immediateFuture(Rpcs.<Void> getRpcResult(true, Collections.<RpcError> emptySet()));
212 * JMX RPC call implemented from the ToasterProviderRuntimeMXBean interface.
215 public void clearToastsMade() {
216 LOG.info( "clearToastsMade" );
221 * Accesssor method implemented from the ToasterProviderRuntimeMXBean interface.
224 public Long getToastsMade() {
225 return toastsMade.get();
228 private void updateStatus() {
229 if (dataProvider != null) {
230 WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
231 tx.put(LogicalDatastoreType.OPERATIONAL,TOASTER_IID, buildToaster());
235 } catch (InterruptedException | ExecutionException e) {
236 LOG.warn("Failed to update toaster status, operational otherwise", e);
239 LOG.trace("No data provider configured, not updating status");
243 private boolean outOfBread()
245 return amountOfBreadInStock.get() == 0;
248 private class MakeToastTask implements Callable<RpcResult<Void>> {
250 final MakeToastInput toastRequest;
252 public MakeToastTask(final MakeToastInput toast) {
253 toastRequest = toast;
257 public RpcResult<Void> call() {
260 // make toast just sleeps for n secondn per doneness level.
261 long darknessFactor = OpendaylightToaster.this.darknessFactor.get();
262 Thread.sleep(darknessFactor * toastRequest.getToasterDoneness());
265 catch( InterruptedException e ) {
266 LOG.info( "Interrupted while making the toast" );
269 toastsMade.incrementAndGet();
271 amountOfBreadInStock.getAndDecrement();
273 LOG.info( "Toaster is out of bread!" );
275 notificationProvider.publish( new ToasterOutOfBreadBuilder().build() );
278 synchronized (taskLock) {
284 LOG.debug("Toast done");
286 return Rpcs.<Void> getRpcResult(true, null, Collections.<RpcError> emptySet());