*/
package org.opendaylight.controller.sal.restconf.impl;
-import java.util.concurrent.Future;
+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 com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
import javax.ws.rs.core.Response.Status;
-
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.md.sal.common.api.data.DataReader;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
-import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
-import org.opendaylight.controller.sal.core.api.data.DataChangeListener;
-import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.core.api.mount.MountInstance;
-import org.opendaylight.controller.sal.rest.impl.RestconfProvider;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class BrokerFacade implements DataReader<InstanceIdentifier, CompositeNode> {
- private final static Logger LOG = LoggerFactory.getLogger( BrokerFacade.class );
+public class BrokerFacade {
+ private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
private final static BrokerFacade INSTANCE = new BrokerFacade();
-
- private volatile DataBrokerService dataService;
private volatile ConsumerSession context;
+ private DOMDataBroker domDataBroker;
private BrokerFacade() {
}
- public void setContext( final ConsumerSession context ) {
+ public void setContext(final ConsumerSession context) {
this.context = context;
}
- public void setDataService( final DataBrokerService dataService ) {
- this.dataService = dataService;
- }
-
public static BrokerFacade getInstance() {
return BrokerFacade.INSTANCE;
}
private void checkPreconditions() {
- if( context == null || dataService == null ) {
- ResponseException _responseException = new ResponseException( Status.SERVICE_UNAVAILABLE,
- RestconfProvider.NOT_INITALIZED_MSG );
- throw _responseException;
+ if (context == null || domDataBroker == null) {
+ throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
}
}
- @Override
- public CompositeNode readConfigurationData( final InstanceIdentifier path ) {
- this.checkPreconditions();
-
- LOG.trace( "Read Configuration via Restconf: {}", path );
-
- return dataService.readConfigurationData( path );
+ // READ configuration
+ public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
+ checkPreconditions();
+ return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
}
- public CompositeNode readConfigurationDataBehindMountPoint( final MountInstance mountPoint,
- final InstanceIdentifier path ) {
- this.checkPreconditions();
-
- LOG.trace( "Read Configuration via Restconf: {}", path );
-
- return mountPoint.readConfigurationData( path );
+ public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
+ }
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
}
- @Override
- public CompositeNode readOperationalData( final InstanceIdentifier path ) {
- this.checkPreconditions();
-
- BrokerFacade.LOG.trace( "Read Operational via Restconf: {}", path );
-
- return dataService.readOperationalData( path );
+ // READ operational
+ public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
+ checkPreconditions();
+ return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
}
- public CompositeNode readOperationalDataBehindMountPoint( final MountInstance mountPoint,
- final InstanceIdentifier path ) {
- this.checkPreconditions();
-
- BrokerFacade.LOG.trace( "Read Operational via Restconf: {}", path );
+ public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), OPERATIONAL, path);
+ }
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
+ }
- return mountPoint.readOperationalData( path );
+ // PUT configuration
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ checkPreconditions();
+ return putDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path, payload);
}
- public RpcResult<CompositeNode> invokeRpc( final QName type, final CompositeNode payload ) {
- this.checkPreconditions();
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
+ final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ return putDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path,
+ payload);
+ }
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
+ }
- final Future<RpcResult<CompositeNode>> future = context.rpc( type, payload );
+ // POST configuration
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ checkPreconditions();
+ return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload);
+ }
- try {
- return future.get();
- }
- catch( Exception e ) {
- throw new ResponseException( e, "Error invoking RPC " + type );
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
+ final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
+ payload);
}
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
}
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataPut( final InstanceIdentifier path,
- final CompositeNode payload ) {
- this.checkPreconditions();
+ // DELETE configuration
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
+ final YangInstanceIdentifier path) {
+ checkPreconditions();
+ return deleteDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path);
+ }
- final DataModificationTransaction transaction = dataService.beginTransaction();
- BrokerFacade.LOG.trace( "Put Configuration via Restconf: {}", path );
- transaction.putConfigurationData( path, payload );
- return transaction.commit();
+ public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
+ final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
+ final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
+ if (domDataBrokerService.isPresent()) {
+ return deleteDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path);
+ }
+ throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
}
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataPutBehindMountPoint(
- final MountInstance mountPoint, final InstanceIdentifier path, final CompositeNode payload ) {
+ // RPC
+ public Future<RpcResult<CompositeNode>> invokeRpc(final QName type, final CompositeNode payload) {
this.checkPreconditions();
- final DataModificationTransaction transaction = mountPoint.beginTransaction();
- BrokerFacade.LOG.trace( "Put Configuration via Restconf: {}", path );
- transaction.putConfigurationData( path, payload );
- return transaction.commit();
+ return context.rpc(type, payload);
}
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataPost( final InstanceIdentifier path,
- final CompositeNode payload) {
+ public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
+ final ListenerAdapter listener) {
this.checkPreconditions();
- final DataModificationTransaction transaction = dataService.beginTransaction();
- /* check for available Node in Configuration DataStore by path */
- CompositeNode availableNode = transaction.readConfigurationData( path );
- if (availableNode != null) {
- String errMsg = "Post Configuration via Restconf was not executed because data already exists";
- BrokerFacade.LOG.warn((new StringBuilder(errMsg)).append(" : ").append(path).toString());
- // FIXME: return correct ietf-restconf:errors -> follow specification
- // (http://tools.ietf.org/html/draft-bierman-netconf-restconf-03#page-48)
- throw new ResponseException(Status.CONFLICT, errMsg);
+ if (listener.isListening()) {
+ return;
}
- BrokerFacade.LOG.trace( "Post Configuration via Restconf: {}", path );
- transaction.putConfigurationData( path, payload );
- return transaction.commit();
- }
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataPostBehindMountPoint(
- final MountInstance mountPoint, final InstanceIdentifier path, final CompositeNode payload ) {
- this.checkPreconditions();
-
- final DataModificationTransaction transaction = mountPoint.beginTransaction();
- /* check for available Node in Configuration DataStore by path */
- CompositeNode availableNode = transaction.readConfigurationData( path );
- if (availableNode != null) {
- String errMsg = "Post Configuration via Restconf was not executed because data already exists";
- BrokerFacade.LOG.warn((new StringBuilder(errMsg)).append(" : ").append(path).toString());
- // FIXME: return correct ietf-restconf:errors -> follow specification
- // (http://tools.ietf.org/html/draft-bierman-netconf-restconf-03#page-48)
- throw new ResponseException(Status.CONFLICT, errMsg);
+ YangInstanceIdentifier path = listener.getPath();
+ final ListenerRegistration<DOMDataChangeListener> registration = domDataBroker.registerDataChangeListener(
+ datastore, path, listener, scope);
+
+ listener.setRegistration(registration);
+ }
+
+ private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
+ LogicalDatastoreType datastore, YangInstanceIdentifier path) {
+ LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
+ final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
+ if (listenableFuture != null) {
+ Optional<NormalizedNode<?, ?>> optional;
+ try {
+ LOG.debug("Reading result data from transaction.");
+ optional = listenableFuture.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
+
+ }
+ if (optional != null) {
+ if (optional.isPresent()) {
+ return optional.get();
+ }
+ }
}
- BrokerFacade.LOG.trace( "Post Configuration via Restconf: {}", path );
- transaction.putConfigurationData( path, payload );
- return transaction.commit();
+ return null;
}
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataDelete( final InstanceIdentifier path ) {
- this.checkPreconditions();
-
- final DataModificationTransaction transaction = dataService.beginTransaction();
- LOG.info( "Delete Configuration via Restconf: {}", path );
- transaction.removeConfigurationData( path );
- return transaction.commit();
+ private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
+ final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ ListenableFuture<Optional<NormalizedNode<?, ?>>> futureDatastoreData = rWTransaction.read(datastore, path);
+ try {
+ final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.get();
+ if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) {
+ String errMsg = "Post Configuration via Restconf was not executed because data already exists";
+ LOG.trace(errMsg + ":{}", path);
+ throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
+ ErrorTag.DATA_EXISTS);
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.trace("It wasn't possible to get data loaded from datastore at path " + path);
+ }
+ rWTransaction.merge(datastore, path, payload);
+ LOG.trace("Post " + datastore.name() + " via Restconf: {}", path);
+ return rWTransaction.submit();
}
- public Future<RpcResult<TransactionStatus>> commitConfigurationDataDeleteBehindMountPoint(
- final MountInstance mountPoint, final InstanceIdentifier path ) {
- this.checkPreconditions();
-
- final DataModificationTransaction transaction = mountPoint.beginTransaction();
- LOG.info( "Delete Configuration via Restconf: {}", path );
- transaction.removeConfigurationData( path );
- return transaction.commit();
+ private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
+ final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+ LOG.trace("Put " + datastore.name() + " via Restconf: {}", path);
+ writeTransaction.put(datastore, path, payload);
+ return writeTransaction.submit();
}
- public void registerToListenDataChanges( final ListenerAdapter listener ) {
- this.checkPreconditions();
-
- if( listener.isListening() ) {
- return;
- }
-
- InstanceIdentifier path = listener.getPath();
- final ListenerRegistration<DataChangeListener> registration =
- dataService.registerDataChangeListener( path, listener );
+ private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
+ final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+ YangInstanceIdentifier path) {
+ LOG.info("Delete " + datastore.name() + " via Restconf: {}", path);
+ writeTransaction.delete(datastore, path);
+ return writeTransaction.submit();
+ }
- listener.setRegistration( registration );
+ public void setDomDataBroker(DOMDataBroker domDataBroker) {
+ this.domDataBroker = domDataBroker;
}
}