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.sal.restconf.impl;
10 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
11 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
12 import com.google.common.base.Optional;
13 import com.google.common.util.concurrent.CheckedFuture;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import java.util.ArrayList;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.Future;
20 import javax.ws.rs.core.Response.Status;
21 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
24 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
25 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
26 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
27 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
28 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
30 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
32 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
33 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
34 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
35 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
36 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
37 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
38 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
39 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
40 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
41 import org.opendaylight.yangtools.concepts.ListenerRegistration;
42 import org.opendaylight.yangtools.yang.common.QName;
43 import org.opendaylight.yangtools.yang.common.RpcResult;
44 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
47 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
48 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
52 public class BrokerFacade {
53 private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
55 private final static BrokerFacade INSTANCE = new BrokerFacade();
56 private volatile DOMRpcService rpcService;
57 private volatile ConsumerSession context;
58 private DOMDataBroker domDataBroker;
60 private BrokerFacade() {
63 public void setRpcService(final DOMRpcService router) {
67 public void setContext(final ConsumerSession context) {
68 this.context = context;
71 public static BrokerFacade getInstance() {
72 return BrokerFacade.INSTANCE;
75 private void checkPreconditions() {
76 if (context == null || domDataBroker == null) {
77 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
82 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
84 return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
87 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
88 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
89 if (domDataBrokerService.isPresent()) {
90 return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
92 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
96 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
98 return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
101 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
102 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
103 if (domDataBrokerService.isPresent()) {
104 return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), OPERATIONAL, path);
106 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
110 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
111 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
112 checkPreconditions();
113 final DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
114 return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
117 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
118 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
119 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
120 if (domDataBrokerService.isPresent()) {
121 final DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
122 return putDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
125 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
128 // POST configuration
129 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
130 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
131 checkPreconditions();
132 final DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
133 return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
136 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
137 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
138 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
139 if (domDataBrokerService.isPresent()) {
140 final DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
141 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
144 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
147 // DELETE configuration
148 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
149 final YangInstanceIdentifier path) {
150 checkPreconditions();
151 return deleteDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path);
154 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
155 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
156 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
157 if (domDataBrokerService.isPresent()) {
158 return deleteDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path);
160 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
164 public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
165 checkPreconditions();
166 if (rpcService == null) {
167 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
169 return rpcService.invokeRpc(type, input);
173 * @deprecated methode has to be removed in Lithium release
176 public Future<RpcResult<CompositeNode>> invokeRpc(final QName type, final CompositeNode payload) {
177 checkPreconditions();
179 return context.rpc(type, payload);
182 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
183 final ListenerAdapter listener) {
184 checkPreconditions();
186 if (listener.isListening()) {
190 final YangInstanceIdentifier path = listener.getPath();
191 final ListenerRegistration<DOMDataChangeListener> registration = domDataBroker.registerDataChangeListener(
192 datastore, path, listener, scope);
194 listener.setRegistration(registration);
197 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
198 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
199 LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
200 final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
201 if (listenableFuture != null) {
202 Optional<NormalizedNode<?, ?>> optional;
204 LOG.debug("Reading result data from transaction.");
205 optional = listenableFuture.get();
206 } catch (InterruptedException | ExecutionException e) {
207 throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
210 if (optional != null) {
211 if (optional.isPresent()) {
212 return optional.get();
219 private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
220 final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
221 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final DataNormalizationOperation<?> root) {
222 final ListenableFuture<Optional<NormalizedNode<?, ?>>> futureDatastoreData = rWTransaction.read(datastore, path);
224 final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.get();
225 if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) {
226 final String errMsg = "Post Configuration via Restconf was not executed because data already exists";
227 LOG.trace(errMsg + ":{}", path);
228 rWTransaction.cancel();
229 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
230 ErrorTag.DATA_EXISTS);
232 } catch (InterruptedException | ExecutionException e) {
233 LOG.trace("It wasn't possible to get data loaded from datastore at path " + path);
236 ensureParentsByMerge(datastore, path, rWTransaction, root);
237 rWTransaction.merge(datastore, path, payload);
238 LOG.trace("Post " + datastore.name() + " via Restconf: {}", path);
239 return rWTransaction.submit();
242 private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
243 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
244 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final DataNormalizationOperation<?> root) {
245 LOG.trace("Put " + datastore.name() + " via Restconf: {}", path);
246 ensureParentsByMerge(datastore, path, writeTransaction, root);
247 writeTransaction.put(datastore, path, payload);
248 return writeTransaction.submit();
251 private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
252 final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
253 final YangInstanceIdentifier path) {
254 LOG.trace("Delete " + datastore.name() + " via Restconf: {}", path);
255 writeTransaction.delete(datastore, path);
256 return writeTransaction.submit();
259 public void setDomDataBroker(final DOMDataBroker domDataBroker) {
260 this.domDataBroker = domDataBroker;
263 private final void ensureParentsByMerge(final LogicalDatastoreType store,
264 final YangInstanceIdentifier normalizedPath, final DOMDataReadWriteTransaction rwTx,
265 final DataNormalizationOperation<?> root) {
266 final List<PathArgument> currentArguments = new ArrayList<>();
267 final Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
268 DataNormalizationOperation<?> currentOp = root;
269 while (iterator.hasNext()) {
270 final PathArgument currentArg = iterator.next();
272 currentOp = currentOp.getChild(currentArg);
273 } catch (final DataNormalizationException e) {
275 throw new IllegalArgumentException(
276 String.format("Invalid child encountered in path %s", normalizedPath), e);
278 currentArguments.add(currentArg);
279 final YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(currentArguments);
281 final Boolean exists;
285 final CheckedFuture<Boolean, ReadFailedException> future = rwTx.exists(store, currentPath);
286 exists = future.checkedGet();
287 } catch (final ReadFailedException e) {
288 LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
290 throw new IllegalStateException("Failed to read pre-existing data", e);
293 if (!exists && iterator.hasNext()) {
294 rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));