BUG 1082 Migrate sal-rest-connector to Async Data Broker API
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / BrokerFacade.java
1 /**
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.sal.restconf.impl;
9
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
13 import com.google.common.base.Optional;
14 import com.google.common.util.concurrent.CheckedFuture;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import java.util.concurrent.ExecutionException;
17 import java.util.concurrent.Future;
18 import javax.ws.rs.core.Response.Status;
19 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
20 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
21 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
22 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
23 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
24 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
25 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
26 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
27 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
28 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
29 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
30 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
31 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
32 import org.opendaylight.yangtools.concepts.ListenerRegistration;
33 import org.opendaylight.yangtools.yang.common.QName;
34 import org.opendaylight.yangtools.yang.common.RpcResult;
35 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 public class BrokerFacade {
42     private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
43
44     private final static BrokerFacade INSTANCE = new BrokerFacade();
45     private volatile ConsumerSession context;
46     private DOMDataBroker domDataBroker;
47
48     private BrokerFacade() {
49     }
50
51     public void setContext(final ConsumerSession context) {
52         this.context = context;
53     }
54
55     public static BrokerFacade getInstance() {
56         return BrokerFacade.INSTANCE;
57     }
58
59     private void checkPreconditions() {
60         if (context == null || domDataBroker == null) {
61             throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
62         }
63     }
64
65     // READ configuration
66     public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
67         checkPreconditions();
68         return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
69     }
70
71     public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
72         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
73         if (domDataBrokerService.isPresent()) {
74             return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
75         }
76         throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
77     }
78
79     // READ operational
80     public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
81         checkPreconditions();
82         return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
83     }
84
85     public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
86         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
87         if (domDataBrokerService.isPresent()) {
88             return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), OPERATIONAL, path);
89         }
90         throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
91     }
92
93     // PUT configuration
94     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
95             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
96         checkPreconditions();
97         return putDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path, payload);
98     }
99
100     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
101             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
102         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
103         if (domDataBrokerService.isPresent()) {
104             return putDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path,
105                     payload);
106         }
107         throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
108     }
109
110     // POST configuration
111     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
112             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
113         checkPreconditions();
114         return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload);
115     }
116
117     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
118             final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
119         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
120         if (domDataBrokerService.isPresent()) {
121             return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
122                     payload);
123         }
124         throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
125     }
126
127     // DELETE configuration
128     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
129             final YangInstanceIdentifier path) {
130         checkPreconditions();
131         return deleteDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path);
132     }
133
134     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
135             final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
136         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
137         if (domDataBrokerService.isPresent()) {
138             return deleteDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path);
139         }
140         throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
141     }
142
143     // RPC
144     public Future<RpcResult<CompositeNode>> invokeRpc(final QName type, final CompositeNode payload) {
145         this.checkPreconditions();
146
147         return context.rpc(type, payload);
148     }
149
150     public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
151             final ListenerAdapter listener) {
152         this.checkPreconditions();
153
154         if (listener.isListening()) {
155             return;
156         }
157
158         YangInstanceIdentifier path = listener.getPath();
159         final ListenerRegistration<DOMDataChangeListener> registration = domDataBroker.registerDataChangeListener(
160                 datastore, path, listener, scope);
161
162         listener.setRegistration(registration);
163     }
164
165     private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
166             LogicalDatastoreType datastore, YangInstanceIdentifier path) {
167         LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
168         final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
169         if (listenableFuture != null) {
170             Optional<NormalizedNode<?, ?>> optional;
171             try {
172                 LOG.debug("Reading result data from transaction.");
173                 optional = listenableFuture.get();
174             } catch (InterruptedException | ExecutionException e) {
175                 throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
176
177             }
178             if (optional != null) {
179                 if (optional.isPresent()) {
180                     return optional.get();
181                 }
182             }
183         }
184         return null;
185     }
186
187     private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
188             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
189             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
190         ListenableFuture<Optional<NormalizedNode<?, ?>>> futureDatastoreData = rWTransaction.read(datastore, path);
191         try {
192             final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.get();
193             if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) {
194                 String errMsg = "Post Configuration via Restconf was not executed because data already exists";
195                 LOG.trace(errMsg + ":{}", path);
196                 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
197                         ErrorTag.DATA_EXISTS);
198             }
199         } catch (InterruptedException | ExecutionException e) {
200             LOG.trace("It wasn't possible to get data loaded from datastore at path " + path);
201         }
202         rWTransaction.merge(datastore, path, payload);
203         LOG.trace("Post " + datastore.name() + " via Restconf: {}", path);
204         return rWTransaction.submit();
205     }
206
207     private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
208             final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
209             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
210         LOG.trace("Put " + datastore.name() + " via Restconf: {}", path);
211         writeTransaction.put(datastore, path, payload);
212         return writeTransaction.submit();
213     }
214
215     private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
216             final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
217             YangInstanceIdentifier path) {
218         LOG.info("Delete " + datastore.name() + " via Restconf: {}", path);
219         writeTransaction.delete(datastore, path);
220         return writeTransaction.submit();
221     }
222
223     public void setDomDataBroker(DOMDataBroker domDataBroker) {
224         this.domDataBroker = domDataBroker;
225     }
226 }