BUG-8056: place an upper bound on number of transactions processed
[controller.git] / opendaylight / md-sal / samples / toaster-provider / src / main / java / org / opendaylight / controller / sample / toaster / provider / OpendaylightToaster.java
index e39ac2baff36836df38392ea2d0f4ee659c50bf0..b876fa2cd3ac15fe21f5dfb84650371d7eebb64d 100644 (file)
@@ -7,6 +7,12 @@
  */
 package org.opendaylight.controller.sample.toaster.provider;
 
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
+import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+import static org.opendaylight.yangtools.yang.common.RpcError.ErrorType.APPLICATION;
+import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.DELETE;
+import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.WRITE;
+
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.AsyncFunction;
@@ -22,17 +28,17 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
-import org.opendaylight.controller.config.yang.config.toaster_provider.impl.ToasterProviderRuntimeMXBean;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.DisplayString;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.MakeToastInput;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.RestockToasterInput;
@@ -43,6 +49,9 @@ import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestocked;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterRestockedBuilder;
 import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.toaster.rev091120.ToasterService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.toaster.app.config.rev160503.ToasterAppConfig;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.toaster.app.config.rev160503.ToasterAppConfigBuilder;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
@@ -51,43 +60,50 @@ import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class OpendaylightToaster implements ToasterService, ToasterProviderRuntimeMXBean,
-                                            DataTreeChangeListener<Toaster>, AutoCloseable {
+public class OpendaylightToaster extends AbstractMXBean
+        implements ToasterService, ToasterProviderRuntimeMXBean, DataTreeChangeListener<Toaster>, AutoCloseable {
 
     private static final Logger LOG = LoggerFactory.getLogger(OpendaylightToaster.class);
 
-    public static final InstanceIdentifier<Toaster> TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
-
+    private static final InstanceIdentifier<Toaster> TOASTER_IID = InstanceIdentifier.builder(Toaster.class).build();
     private static final DisplayString TOASTER_MANUFACTURER = new DisplayString("Opendaylight");
     private static final DisplayString TOASTER_MODEL_NUMBER = new DisplayString("Model 1 - Binding Aware");
 
-    private NotificationProviderService notificationProvider;
     private DataBroker dataProvider;
+    private NotificationPublishService notificationProvider;
+    private ListenerRegistration<OpendaylightToaster> dataTreeChangeListenerRegistration;
 
     private final ExecutorService executor;
 
-    // The following holds the Future for the current make toast task.
-    // This is used to cancel the current toast.
+    // This holds the Future for the current make toast task and is used to cancel the current toast.
     private final AtomicReference<Future<?>> currentMakeToastTask = new AtomicReference<>();
 
+    // Thread safe holders
     private final AtomicLong amountOfBreadInStock = new AtomicLong( 100 );
-
     private final AtomicLong toastsMade = new AtomicLong(0);
-
-    // Thread safe holder for our darkness multiplier.
     private final AtomicLong darknessFactor = new AtomicLong( 1000 );
 
+    private final ToasterAppConfig toasterAppConfig;
+
     public OpendaylightToaster() {
+        this(new ToasterAppConfigBuilder().setManufacturer(TOASTER_MANUFACTURER).setModelNumber(TOASTER_MODEL_NUMBER)
+                .setMaxMakeToastTries(2).build());
+    }
+
+    public OpendaylightToaster(ToasterAppConfig toasterAppConfig) {
+        super("OpendaylightToaster", "toaster-provider", null);
         executor = Executors.newFixedThreadPool(1);
+        this.toasterAppConfig = toasterAppConfig;
     }
 
-    public void setNotificationProvider(final NotificationProviderService salService) {
-        this.notificationProvider = salService;
+    public void setNotificationProvider(final NotificationPublishService notificationPublishService) {
+        this.notificationProvider = notificationPublishService;
     }
 
     public void setDataProvider(final DataBroker salDataProvider) {
         this.dataProvider = salDataProvider;
-        setToasterStatusUp( null );
+        dataProvider.registerDataTreeChangeListener(new DataTreeIdentifier<>(CONFIGURATION, TOASTER_IID), this);
+        setToasterStatusUp(null);
     }
 
     /**
@@ -99,8 +115,10 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
         executor.shutdown();
 
         if (dataProvider != null) {
+            dataTreeChangeListenerRegistration.close();
+
             WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
-            tx.delete(LogicalDatastoreType.OPERATIONAL,TOASTER_IID);
+            tx.delete(OPERATIONAL,TOASTER_IID);
             Futures.addCallback( tx.submit(), new FutureCallback<Void>() {
                 @Override
                 public void onSuccess( final Void result ) {
@@ -116,12 +134,11 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
     }
 
     private Toaster buildToaster( final ToasterStatus status ) {
-
         // note - we are simulating a device whose manufacture and model are
         // fixed (embedded) into the hardware.
         // This is why the manufacture and model number are hardcoded.
-        return new ToasterBuilder().setToasterManufacturer( TOASTER_MANUFACTURER )
-                                   .setToasterModelNumber( TOASTER_MODEL_NUMBER )
+        return new ToasterBuilder().setToasterManufacturer( toasterAppConfig.getManufacturer() )
+                                   .setToasterModelNumber( toasterAppConfig.getModelNumber() )
                                    .setToasterStatus( status )
                                    .build();
     }
@@ -133,7 +150,7 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
     public void onDataTreeChanged(Collection<DataTreeModification<Toaster>> changes) {
         for(DataTreeModification<Toaster> change: changes) {
             DataObjectModification<Toaster> rootNode = change.getRootNode();
-            if(rootNode.getModificationType() == DataObjectModification.ModificationType.WRITE) {
+            if(rootNode.getModificationType() == WRITE) {
                 Toaster oldToaster = rootNode.getDataBefore();
                 Toaster newToaster = rootNode.getDataAfter();
                 LOG.info("onDataTreeChanged - Toaster config with path {} was added or replaced: old Toaster: {}, new Toaster: {}",
@@ -143,7 +160,7 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
                 if(darkness != null) {
                     darknessFactor.set(darkness);
                 }
-            } else if(rootNode.getModificationType() == DataObjectModification.ModificationType.DELETE) {
+            } else if(rootNode.getModificationType() == DELETE) {
                 LOG.info("onDataTreeChanged - Toaster config with path {} was deleted: old Toaster: {}",
                         change.getRootPath().getRootIdentifier(), rootNode.getDataBefore());
             }
@@ -151,18 +168,16 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
     }
 
     /**
-     * RPC call implemented from the ToasterService interface that cancels the current
-     * toast, if any.
+     * RPC call implemented from the ToasterService interface that cancels the current toast, if any.
      */
     @Override
     public Future<RpcResult<Void>> cancelToast() {
-
         Future<?> current = currentMakeToastTask.getAndSet( null );
         if( current != null ) {
             current.cancel( true );
         }
 
-        // Always return success from the cancel toast call.
+        // Always return success from the cancel toast call
         return Futures.immediateFuture( RpcResultBuilder.<Void> success().build() );
     }
 
@@ -175,18 +190,18 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
 
         final SettableFuture<RpcResult<Void>> futureResult = SettableFuture.create();
 
-        checkStatusAndMakeToast( input, futureResult, 2 );
+        checkStatusAndMakeToast( input, futureResult, toasterAppConfig.getMaxMakeToastTries() );
 
         return futureResult;
     }
 
     private RpcError makeToasterOutOfBreadError() {
-        return RpcResultBuilder.newError( ErrorType.APPLICATION, "resource-denied",
+        return RpcResultBuilder.newError( APPLICATION, "resource-denied",
                 "Toaster is out of bread", "out-of-stock", null, null );
     }
 
     private RpcError makeToasterInUseError() {
-        return RpcResultBuilder.newWarning( ErrorType.APPLICATION, "in-use",
+        return RpcResultBuilder.newWarning( APPLICATION, "in-use",
                 "Toaster is busy", null, null, null );
     }
 
@@ -199,15 +214,10 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
         // to make toast.
 
         final ReadWriteTransaction tx = dataProvider.newReadWriteTransaction();
-        ListenableFuture<Optional<Toaster>> readFuture =
-                                          tx.read( LogicalDatastoreType.OPERATIONAL, TOASTER_IID );
+        ListenableFuture<Optional<Toaster>> readFuture = tx.read(OPERATIONAL, TOASTER_IID);
 
         final ListenableFuture<Void> commitFuture =
-            Futures.transform( readFuture, new AsyncFunction<Optional<Toaster>,Void>() {
-
-                @Override
-                public ListenableFuture<Void> apply(
-                        final Optional<Toaster> toasterData ) throws Exception {
+            Futures.transform( readFuture, (AsyncFunction<Optional<Toaster>, Void>) toasterData -> {
 
                     ToasterStatus toasterStatus = ToasterStatus.Up;
                     if( toasterData.isPresent() ) {
@@ -230,7 +240,7 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
                         // We're not currently making toast - try to update the status to Down
                         // to indicate we're going to make toast. This acts as a lock to prevent
                         // concurrent toasting.
-                        tx.put( LogicalDatastoreType.OPERATIONAL, TOASTER_IID,
+                        tx.put( OPERATIONAL, TOASTER_IID,
                                 buildToaster( ToasterStatus.Down ) );
                         return tx.submit();
                     }
@@ -242,7 +252,6 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
                     // TransactionStatus in the RpcResult as an error condition.
                     return Futures.immediateFailedCheckedFuture(
                             new TransactionCommitFailedException( "", makeToasterInUseError() ) );
-                }
         } );
 
         Futures.addCallback( commitFuture, new FutureCallback<Void>() {
@@ -297,7 +306,7 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
         if( amountOfBreadInStock.get() > 0 ) {
             ToasterRestocked reStockedNotification = new ToasterRestockedBuilder()
                 .setAmountOfBread( input.getAmountOfBreadToStock() ).build();
-            notificationProvider.publish( reStockedNotification );
+            notificationProvider.offerNotification( reStockedNotification );
         }
 
         return Futures.immediateFuture( RpcResultBuilder.<Void> success().build() );
@@ -321,9 +330,8 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
     }
 
     private void setToasterStatusUp( final Function<Boolean,Void> resultCallback ) {
-
         WriteTransaction tx = dataProvider.newWriteOnlyTransaction();
-        tx.put( LogicalDatastoreType.OPERATIONAL,TOASTER_IID, buildToaster( ToasterStatus.Up ) );
+        tx.put( OPERATIONAL,TOASTER_IID, buildToaster( ToasterStatus.Up ) );
 
         Futures.addCallback( tx.submit(), new FutureCallback<Void>() {
             @Override
@@ -383,16 +391,14 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
             if( outOfBread() ) {
                 LOG.info( "Toaster is out of bread!" );
 
-                notificationProvider.publish( new ToasterOutOfBreadBuilder().build() );
+                notificationProvider.offerNotification( new ToasterOutOfBreadBuilder().build() );
             }
 
             // Set the Toaster status back to up - this essentially releases the toasting lock.
             // We can't clear the current toast task nor set the Future result until the
             // update has been committed so we pass a callback to be notified on completion.
 
-            setToasterStatusUp( new Function<Boolean,Void>() {
-                @Override
-                public Void apply( final Boolean result ) {
+            setToasterStatusUp( result -> {
 
                     currentMakeToastTask.set( null );
 
@@ -401,7 +407,6 @@ public class OpendaylightToaster implements ToasterService, ToasterProviderRunti
                     futureResult.set( RpcResultBuilder.<Void>success().build() );
 
                     return null;
-                }
             } );
 
             return null;