Bug 5553 - Impl get operations
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / netconf / sal / restconf / impl / RestconfImpl.java
index 4389224583598c18a44b0eb3a5b0a6724bcc3fc9..095df06586d5e14d0811720c0e97a6a0cd698858 100644 (file)
@@ -11,15 +11,14 @@ package org.opendaylight.netconf.sal.restconf.impl;
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Predicates;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
-import com.google.common.base.Throwables;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import java.math.BigInteger;
 import java.net.URI;
@@ -31,11 +30,13 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
@@ -71,12 +72,12 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
@@ -96,6 +97,7 @@ import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveSchemaContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -321,9 +323,48 @@ public class RestconfImpl implements RestconfService {
         return operationsFromModulesToNormalizedContext(modules, mountPoint);
     }
 
+    /**
+     * Special case only for GET restconf/operations use (since moment of pre-Beryllium
+     * Yang parser and Yang model API removal). The method is creating fake
+     * schema context with fake module and fake data by use own implementations
+     * of schema nodes and module.
+     *
+     * @param modules
+     *            - set of modules for get RPCs from every module
+     * @param mountPoint
+     *            - mount point, if in use otherwise null
+     * @return {@link NormalizedNodeContext}
+     */
     private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set<Module> modules,
             final DOMMountPoint mountPoint) {
-        throw new UnsupportedOperationException();
+
+        final ContainerSchemaNodeImpl fakeCont = new ContainerSchemaNodeImpl();
+        final List<LeafNode<Object>> listRpcNodes = new ArrayList<>();
+        for (final Module m : modules) {
+            for (final RpcDefinition rpc : m.getRpcs()) {
+
+                final LeafSchemaNode fakeLeaf = new LeafSchemaNodeImpl(fakeCont.getPath(),
+                        QName.create(ModuleImpl.moduleQName, m.getName() + ":" + rpc.getQName().getLocalName()));
+                fakeCont.addNodeChild(fakeLeaf);
+                listRpcNodes.add(Builders.leafBuilder(fakeLeaf).build());
+            }
+        }
+        final ContainerSchemaNode fakeContSchNode = fakeCont;
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders
+                .containerBuilder(fakeContSchNode);
+
+        for (final LeafNode<Object> rpcNode : listRpcNodes) {
+            containerBuilder.withChild(rpcNode);
+        }
+
+        final Module fakeModule = new ModuleImpl(fakeContSchNode);
+
+        final Set<Module> fakeModules = new HashSet<>();
+        fakeModules.add(fakeModule);
+        final SchemaContext fakeSchemaCtx = EffectiveSchemaContext.resolveSchemaContext(fakeModules);
+        final InstanceIdentifierContext<ContainerSchemaNode> instanceIdentifierContext = new InstanceIdentifierContext<>(
+                null, fakeContSchNode, mountPoint, fakeSchemaCtx);
+        return new NormalizedNodeContext(instanceIdentifierContext, containerBuilder.build());
     }
 
     private Module getRestconfModule() {
@@ -466,12 +507,6 @@ public class RestconfImpl implements RestconfService {
             // did not expect any input
             throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
         }
-        // else
-        // {
-        // TODO: Validate "mandatory" and "config" values here??? Or should those be
-        // those be
-        // validate in a more central location inside MD-SAL core.
-        // }
     }
 
     private CheckedFuture<DOMRpcResult, DOMRpcException> invokeSalRemoteRpcSubscribeRPC(final NormalizedNodeContext payload) {
@@ -672,36 +707,67 @@ public class RestconfImpl implements RestconfService {
          * failures back to the client and forcing them to handle it via retry (and having to
          * document the behavior).
          */
-        int tries = 2;
+        PutResult result = null;
+        final TryOfPutData tryPutData = new TryOfPutData();
         while(true) {
-            try {
-                if (mountPoint != null) {
-                    this.broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet();
-                } else {
-                    this.broker.commitConfigurationDataPut(this.controllerContext.getGlobalSchema(), normalizedII, payload.getData()).checkedGet();
+            if (mountPoint != null) {
+
+                result = this.broker.commitMountPointDataPut(mountPoint, normalizedII, payload.getData());
+            } else {
+                result = this.broker.commitConfigurationDataPut(this.controllerContext.getGlobalSchema(), normalizedII,
+                        payload.getData());
+            }
+            final CountDownLatch waiter = new CountDownLatch(1);
+            Futures.addCallback(result.getFutureOfPutData(), new FutureCallback<Void>() {
+
+                @Override
+                public void onSuccess(final Void result) {
+                    handlingLoggerPut(null, tryPutData, identifier);
+                    waiter.countDown();
+                }
+
+                @Override
+                public void onFailure(final Throwable t) {
+                    waiter.countDown();
+                    handlingLoggerPut(t, tryPutData, identifier);
                 }
+            });
 
+            try {
+                waiter.await();
+            } catch (final InterruptedException e) {
+                final String msg = "Problem while waiting for response";
+                LOG.warn(msg);
+                throw new RestconfDocumentedException(msg, e);
+            }
+
+            if(tryPutData.isDone()){
                 break;
-            } catch (final TransactionCommitFailedException e) {
-                if(e instanceof OptimisticLockFailedException) {
-                    if(--tries <= 0) {
-                        LOG.debug("Got OptimisticLockFailedException on last try - failing " + identifier);
-                        throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
-                    }
+            } else {
+                throw new RestconfDocumentedException("Problem while PUT operations");
+            }
+        }
 
-                    LOG.debug("Got OptimisticLockFailedException - trying again " + identifier);
-                } else {
-                    LOG.debug("Update ConfigDataStore fail " + identifier, e);
-                    throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
+        return Response.status(result.getStatus()).build();
+    }
+
+    protected void handlingLoggerPut(final Throwable t, final TryOfPutData tryPutData, final String identifier) {
+        if (t != null) {
+            if (t instanceof OptimisticLockFailedException) {
+                if (tryPutData.countGet() <= 0) {
+                    LOG.debug("Got OptimisticLockFailedException on last try - failing " + identifier);
+                    throw new RestconfDocumentedException(t.getMessage(), t);
                 }
-            } catch (final Exception e) {
-                final String errMsg = "Error updating data ";
-                LOG.debug(errMsg + identifier, e);
-                throw new RestconfDocumentedException(errMsg, e);
+                LOG.debug("Got OptimisticLockFailedException - trying again " + identifier);
+                tryPutData.countDown();
+            } else {
+                LOG.debug("Update ConfigDataStore fail " + identifier, t);
+                throw new RestconfDocumentedException(t.getMessage(), t);
             }
+        } else {
+            LOG.trace("PUT Successful " + identifier);
+            tryPutData.done();
         }
-
-        return Response.status(Status.OK).build();
     }
 
     private static void validateTopLevelNodeName(final NormalizedNodeContext node,
@@ -822,18 +888,37 @@ public class RestconfImpl implements RestconfService {
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
         final InstanceIdentifierContext<?> iiWithData = payload.getInstanceIdentifierContext();
         final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
-        try {
-            if (mountPoint != null) {
-                this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()).checkedGet();
-            } else {
-                this.broker.commitConfigurationDataPost(this.controllerContext.getGlobalSchema(), normalizedII, payload.getData()).checkedGet();
+
+        CheckedFuture<Void, TransactionCommitFailedException> future;
+        if (mountPoint != null) {
+            future = this.broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData());
+        } else {
+            future = this.broker.commitConfigurationDataPost(this.controllerContext.getGlobalSchema(), normalizedII,
+                    payload.getData());
+        }
+
+        final CountDownLatch waiter = new CountDownLatch(1);
+        Futures.addCallback(future, new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(final Void result) {
+                handlerLoggerPost(null, uriInfo);
+                waiter.countDown();
             }
-        } catch(final RestconfDocumentedException e) {
-            throw e;
-        } catch (final Exception e) {
-            final String errMsg = "Error creating data ";
-            LOG.info(errMsg + (uriInfo != null ? uriInfo.getPath() : ""), e);
-            throw new RestconfDocumentedException(errMsg, e);
+
+            @Override
+            public void onFailure(final Throwable t) {
+                waiter.countDown();
+                handlerLoggerPost(t, uriInfo);
+            }
+        });
+
+        try {
+            waiter.await();
+        } catch (final InterruptedException e) {
+            final String msg = "Problem while waiting for response";
+            LOG.warn(msg);
+            throw new RestconfDocumentedException(msg, e);
         }
 
         final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT);
@@ -845,6 +930,16 @@ public class RestconfImpl implements RestconfService {
         return responseBuilder.build();
     }
 
+    protected void handlerLoggerPost(final Throwable t, final UriInfo uriInfo) {
+        if (t != null) {
+            final String errMsg = "Error creating data ";
+            LOG.warn(errMsg + (uriInfo != null ? uriInfo.getPath() : ""), t);
+            throw new RestconfDocumentedException(errMsg, t);
+        } else {
+            LOG.trace("Successfuly create data.");
+        }
+    }
+
     private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) {
         if(uriInfo == null) {
             // This is null if invoked internally
@@ -868,25 +963,51 @@ public class RestconfImpl implements RestconfService {
         final DOMMountPoint mountPoint = iiWithData.getMountPoint();
         final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier();
 
-        try {
-            if (mountPoint != null) {
-                this.broker.commitConfigurationDataDelete(mountPoint, normalizedII);
-            } else {
-                this.broker.commitConfigurationDataDelete(normalizedII).get();
+        final CheckedFuture<Void, TransactionCommitFailedException> future;
+        if (mountPoint != null) {
+            future = this.broker.commitConfigurationDataDelete(mountPoint, normalizedII);
+        } else {
+            future = this.broker.commitConfigurationDataDelete(normalizedII);
+        }
+
+        final CountDownLatch waiter = new CountDownLatch(1);
+        Futures.addCallback(future, new FutureCallback<Void>() {
+
+            @Override
+            public void onSuccess(final Void result) {
+                handlerLoggerDelete(null);
+                waiter.countDown();
             }
-        } catch (final Exception e) {
-            final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
-                    Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
-            if (searchedException.isPresent()) {
-                throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
+
+            @Override
+            public void onFailure(final Throwable t) {
+                waiter.countDown();
+                handlerLoggerDelete(t);
             }
-            final String errMsg = "Error while deleting data";
-            LOG.info(errMsg, e);
-            throw new RestconfDocumentedException(errMsg, e);
+
+        });
+
+        try {
+            waiter.await();
+        } catch (final InterruptedException e) {
+            final String msg = "Problem while waiting for response";
+            LOG.warn(msg);
+            throw new RestconfDocumentedException(msg, e);
         }
+
         return Response.status(Status.OK).build();
     }
 
+    protected void handlerLoggerDelete(final Throwable t) {
+        if (t != null) {
+            final String errMsg = "Error while deleting data";
+            LOG.info(errMsg, t);
+            throw new RestconfDocumentedException(errMsg, t);
+        } else {
+            LOG.trace("Successfuly delete data.");
+        }
+    }
+
     /**
      * Subscribes to some path in schema context (stream) to listen on changes on this stream.
      *
@@ -1088,11 +1209,6 @@ public class RestconfImpl implements RestconfService {
         return result;
     }
 
-    public BigInteger getOperationalReceived() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
     private MapNode makeModuleMapNode(final Set<Module> modules) {
         Preconditions.checkNotNull(modules);
         final Module restconfModule = getRestconfModule();
@@ -1262,4 +1378,24 @@ public class RestconfImpl implements RestconfService {
 
         return Futures.immediateCheckedFuture(defaultDOMRpcResult);
     }
+
+    private class TryOfPutData {
+        int tries = 2;
+        boolean done = false;
+
+        void countDown() {
+            this.tries--;
+        }
+
+        void done() {
+            this.done = true;
+        }
+
+        boolean isDone() {
+            return this.done;
+        }
+        int countGet() {
+            return this.tries;
+        }
+    }
 }