import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
+import javax.annotation.PreDestroy;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean;
import org.opendaylight.mdsal.binding.api.DataBroker;
import org.opendaylight.mdsal.binding.api.DataObjectModification;
import org.opendaylight.mdsal.binding.api.DataTreeModification;
import org.opendaylight.mdsal.binding.api.NotificationPublishService;
import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
+import org.opendaylight.mdsal.binding.api.RpcProviderService;
import org.opendaylight.mdsal.binding.api.WriteTransaction;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.common.api.OptimisticLockFailedException;
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.concepts.Registration;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
-import org.opendaylight.yangtools.yang.common.Uint16;
import org.opendaylight.yangtools.yang.common.Uint32;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class OpendaylightToaster extends AbstractMXBean
+@Singleton
+@Component(service = ToasterService.class, immediate = true)
+@Designate(ocd = OpendaylightToaster.Configuration.class)
+public final class OpendaylightToaster extends AbstractMXBean
implements ToasterService, ToasterProviderRuntimeMXBean, DataTreeChangeListener<Toaster>, AutoCloseable {
+ @ObjectClassDefinition
+ public @interface Configuration {
+ @AttributeDefinition(description = "The name of the toaster's manufacturer", max = "255")
+ String manufacturer() default TOASTER_MANUFACTURER;
+ @AttributeDefinition(description = "The name of the toaster's model", max = "255")
+ String modelNumber() default TOASTER_MODEL_NUMBER;
+ @AttributeDefinition(description = "How many times we attempt to make toast before failing ",
+ min = "0", max = "65535")
+ int maxMakeToastTries() default 2;
+ }
private static final CancelToastOutput EMPTY_CANCEL_OUTPUT = new CancelToastOutputBuilder().build();
private static final MakeToastOutput EMPTY_MAKE_OUTPUT = new MakeToastOutputBuilder().build();
private static final Logger LOG = LoggerFactory.getLogger(OpendaylightToaster.class);
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 static final String TOASTER_MANUFACTURER = "Opendaylight";
+ private static final String TOASTER_MODEL_NUMBER = "Model 1 - Binding Aware";
- private DataBroker dataBroker;
- private NotificationPublishService notificationProvider;
- private ListenerRegistration<OpendaylightToaster> dataTreeChangeListenerRegistration;
+ private final DataBroker dataBroker;
+ private final NotificationPublishService notificationProvider;
+ private final ListenerRegistration<OpendaylightToaster> dataTreeChangeListenerRegistration;
+ private final Registration reg;
private final ExecutorService executor;
private final AtomicLong toastsMade = new AtomicLong(0);
private final AtomicLong darknessFactor = new AtomicLong(1000);
- private final ToasterAppConfig toasterAppConfig;
-
- public OpendaylightToaster() {
- this(new ToasterAppConfigBuilder().setManufacturer(TOASTER_MANUFACTURER).setModelNumber(TOASTER_MODEL_NUMBER)
- .setMaxMakeToastTries(Uint16.valueOf(2)).build());
- }
+ private final @NonNull DisplayString manufacturer;
+ private final @NonNull DisplayString modelNumber;
+ private final int maxMakeToastTries;
- public OpendaylightToaster(final ToasterAppConfig toasterAppConfig) {
+ public OpendaylightToaster(final DataBroker dataProvider,
+ final NotificationPublishService notificationPublishService, final RpcProviderService rpcProviderService,
+ final String manufacturer, final String modelNumber, final int maxMakeToastTries) {
super("OpendaylightToaster", "toaster-provider", null);
- executor = Executors.newFixedThreadPool(1);
- this.toasterAppConfig = toasterAppConfig;
- }
+ notificationProvider = requireNonNull(notificationPublishService);
+ dataBroker = requireNonNull(dataProvider);
- public void setNotificationProvider(final NotificationPublishService notificationPublishService) {
- notificationProvider = notificationPublishService;
- }
+ this.manufacturer = new DisplayString(manufacturer);
+ this.modelNumber = new DisplayString(modelNumber);
+ this.maxMakeToastTries = maxMakeToastTries;
- public void setDataBroker(final DataBroker dataBroker) {
- this.dataBroker = dataBroker;
- }
+ executor = Executors.newFixedThreadPool(1);
+ reg = rpcProviderService.registerRpcImplementation(ToasterService.class, this);
- public void init() {
LOG.info("Initializing...");
dataTreeChangeListenerRegistration = requireNonNull(dataBroker, "dataBroker must be set")
.registerDataTreeChangeListener(DataTreeIdentifier.create(CONFIGURATION, TOASTER_IID), this);
- setToasterStatusUp(null);
+ try {
+ setToasterStatusUp(null).get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new IllegalStateException("Failed to commit initial data", e);
+ }
// Register our MXBean.
register();
}
+ @Inject
+ public OpendaylightToaster(final DataBroker dataProvider,
+ final NotificationPublishService notificationPublishService, final RpcProviderService rpcProviderService) {
+ this(dataProvider, notificationPublishService, rpcProviderService, TOASTER_MANUFACTURER, TOASTER_MODEL_NUMBER,
+ 2);
+ }
+
+ @Activate
+ public OpendaylightToaster(@Reference final DataBroker dataProvider,
+ @Reference final NotificationPublishService notificationPublishService,
+ @Reference final RpcProviderService rpcProviderService, final @NonNull Configuration configuration) {
+ this(dataProvider, notificationPublishService, rpcProviderService, configuration.manufacturer(),
+ configuration.modelNumber(), configuration.maxMakeToastTries());
+ }
+
/**
* Implemented from the AutoCloseable interface.
*/
@Override
+ @PreDestroy
+ @Deactivate
public void close() {
LOG.info("Closing...");
// Unregister our MXBean.
unregister();
+ reg.close();
// When we close this service we need to shutdown our executor!
executor.shutdown();
// 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(toasterAppConfig.getManufacturer())
- .setToasterModelNumber(toasterAppConfig.getModelNumber()).setToasterStatus(status).build();
+ return new ToasterBuilder()
+ .setToasterManufacturer(manufacturer)
+ .setToasterModelNumber(modelNumber)
+ .setToasterStatus(status)
+ .build();
}
/**
final SettableFuture<RpcResult<MakeToastOutput>> futureResult = SettableFuture.create();
- checkStatusAndMakeToast(input, futureResult, toasterAppConfig.getMaxMakeToastTries().toJava());
+ checkStatusAndMakeToast(input, futureResult, maxMakeToastTries);
return futureResult;
}
return toastsMade.get();
}
- private void setToasterStatusUp(final Function<Boolean, MakeToastOutput> resultCallback) {
+ private ListenableFuture<?> setToasterStatusUp(final Function<Boolean, MakeToastOutput> resultCallback) {
WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
tx.put(OPERATIONAL,TOASTER_IID, buildToaster(ToasterStatus.Up));
- Futures.addCallback(tx.commit(), new FutureCallback<CommitInfo>() {
+ final var future = tx.commit();
+ Futures.addCallback(future, new FutureCallback<CommitInfo>() {
@Override
public void onSuccess(final CommitInfo result) {
LOG.info("Successfully set ToasterStatus to Up");
}
}
}, MoreExecutors.directExecutor());
+
+ return future;
}
private boolean outOfBread() {
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
- xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
- xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
- odl:restart-dependents-on-updates="true" odl:use-default-for-reference-types="true">
-
- <!-- "restart-dependents-on-updates" is an ODL extension attribute that processes any "property-placeholder"
- elements and reacts to updates to the corresponding cfg file by restarting this blueprint container any
- dependent containers that consume OSGi services provided by this container in an atomic and orderly
- manner.
-
- "use-default-for-reference-types" is an ODL extension attribute that adds a filter to all services
- imported via "reference" elements where the "type" property is either not set or set to "default" if
- the odl:type attribute isn't explicitly specified. This ensures the default implementation is imported
- if there are other implementations advertised with other types.
- -->
-
- <!-- Accesses properties via the etc/org.opendaylight.toaster.cfg file. The properties are made available
- as variables that can be referenced. The variables are substituted with the actual values read from
- the cfg file, if present, or the default-properties.
- -->
- <cm:property-placeholder persistent-id="org.opendaylight.toaster" update-strategy="none">
- <cm:default-properties>
- <cm:property name="databroker-type" value="default"/>
- </cm:default-properties>
- </cm:property-placeholder>
-
- <!-- "clustered-app-config" is an ODL extension that obtains an application configuration yang container
- from the MD-SAL data store and makes the binding DataObject available as a bean that can be injected
- into other beans. Here we obtain the ToasterAppConfig container DataObject. This also shows how to
- specify default data via the "default-config" child element. While default leaf values defined in the
- yang are returned, one may have more complex data, eg lists, that require default data. The
- "default-config" must contain the XML representation of the yang data, including namespace, wrapped
- in a CDATA section to prevent the blueprint container from treating it as markup.
- -->
- <odl:clustered-app-config id="toasterAppConfig"
- binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.toaster.app.config.rev160503.ToasterAppConfig">
- <odl:default-config><![CDATA[
- <toaster-app-config xmlns="urn:opendaylight:params:xml:ns:yang:controller:toaster-app-config">
- <max-make-toast-tries>3</max-make-toast-tries>
- </toaster-app-config>
- ]]></odl:default-config>
- </odl:clustered-app-config>
-
- <!-- Import MD-SAL services. For the DataBroker, we explicitly specify the odl:type which is configurable
- via the cfg file. In this manner the toaster can be configured to use the default clustered DataBroker
- or the specialized "pingpong" DataBroker (or any other DataBroker implementation).
- -->
- <reference id="dataBroker" interface="org.opendaylight.mdsal.binding.api.DataBroker" odl:type="${databroker-type}" />
- <reference id="notificationService" interface="org.opendaylight.mdsal.binding.api.NotificationPublishService"/>
-
- <!-- Create the OpendaylightToaster instance and inject its dependencies -->
- <bean id="toaster" class="org.opendaylight.controller.sample.toaster.provider.OpendaylightToaster"
- init-method="init" destroy-method="close">
- <argument ref="toasterAppConfig"/>
- <property name="dataBroker" ref="dataBroker"/>
- <property name="notificationProvider" ref="notificationService"/>
- </bean>
-
- <!-- Register the OpendaylightToaster instance as an RPC implementation provider. The "rpc-implementation"
- element automatically figures out the RpcService interface although it can be explicitly specified.
- -->
- <odl:rpc-implementation ref="toaster"/>
-</blueprint>