2 * Copyright (c) 2016 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.restconf.restful.services.impl;
10 import static org.opendaylight.restconf.restful.utils.RestconfStreamsConstants.CREATE_NOTIFICATION_STREAM;
11 import static org.opendaylight.restconf.restful.utils.RestconfStreamsConstants.STREAM_ACCESS_PATH_PART;
12 import static org.opendaylight.restconf.restful.utils.RestconfStreamsConstants.STREAM_LOCATION_PATH_PART;
13 import static org.opendaylight.restconf.restful.utils.RestconfStreamsConstants.STREAM_PATH;
15 import com.google.common.base.Optional;
16 import com.google.common.base.Preconditions;
17 import java.text.SimpleDateFormat;
18 import java.util.Date;
19 import java.util.List;
20 import java.util.Map.Entry;
21 import java.util.TimeZone;
22 import javax.annotation.Nonnull;
23 import javax.ws.rs.core.Response;
24 import javax.ws.rs.core.UriInfo;
25 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
26 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
27 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
28 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
29 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
30 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
31 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
32 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
33 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
34 import org.opendaylight.netconf.sal.restconf.impl.WriterParameters;
35 import org.opendaylight.restconf.RestConnectorProvider;
36 import org.opendaylight.restconf.common.references.SchemaContextRef;
37 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
38 import org.opendaylight.restconf.handlers.SchemaContextHandler;
39 import org.opendaylight.restconf.handlers.TransactionChainHandler;
40 import org.opendaylight.restconf.restful.services.api.RestconfDataService;
41 import org.opendaylight.restconf.restful.services.api.RestconfStreamsSubscriptionService;
42 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
43 import org.opendaylight.restconf.restful.utils.DeleteDataTransactionUtil;
44 import org.opendaylight.restconf.restful.utils.PatchDataTransactionUtil;
45 import org.opendaylight.restconf.restful.utils.PostDataTransactionUtil;
46 import org.opendaylight.restconf.restful.utils.PutDataTransactionUtil;
47 import org.opendaylight.restconf.restful.utils.ReadDataTransactionUtil;
48 import org.opendaylight.restconf.restful.utils.RestconfDataServiceConstant;
49 import org.opendaylight.restconf.utils.RestconfConstants;
50 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
51 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
52 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 * Implementation of {@link RestconfDataService}
59 public class RestconfDataServiceImpl implements RestconfDataService {
61 private final static Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class);
63 private final SchemaContextHandler schemaContextHandler;
64 private final TransactionChainHandler transactionChainHandler;
65 private final DOMMountPointServiceHandler mountPointServiceHandler;
67 private final RestconfStreamsSubscriptionService delegRestconfSubscrService;
69 public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler,
70 final TransactionChainHandler transactionChainHandler,
71 final DOMMountPointServiceHandler mountPointServiceHandler,
72 final RestconfStreamsSubscriptionService delegRestconfSubscrService) {
73 this.schemaContextHandler = schemaContextHandler;
74 this.transactionChainHandler = transactionChainHandler;
75 this.mountPointServiceHandler = mountPointServiceHandler;
76 this.delegRestconfSubscrService = delegRestconfSubscrService;
80 public Response readData(final UriInfo uriInfo) {
81 return readData(null, uriInfo);
85 public Response readData(final String identifier, final UriInfo uriInfo) {
86 final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
87 final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
88 identifier, schemaContextRef.get(), Optional.of(this.mountPointServiceHandler.get()));
90 boolean withDefa_used = false;
91 String withDefa = null;
93 for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
94 switch (entry.getKey()) {
98 withDefa = entry.getValue().iterator().next();
100 throw new RestconfDocumentedException("With-defaults parameter can be used only once.");
105 boolean tagged = false;
107 if (withDefa.equals("report-all-tagged")) {
111 if (withDefa.equals("report-all")) {
116 final WriterParameters parameters = ReadDataTransactionUtil.parseUriParameters(
117 instanceIdentifier, uriInfo, tagged);
119 final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
120 final DOMTransactionChain transactionChain;
121 if (mountPoint == null) {
122 transactionChain = this.transactionChainHandler.get();
124 transactionChain = transactionChainOfMountPoint(mountPoint);
127 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
128 instanceIdentifier, mountPoint, transactionChain);
129 final NormalizedNode<?, ?> node =
130 ReadDataTransactionUtil.readData(identifier, parameters.getContent(), transactionNode, withDefa,
131 schemaContextRef, uriInfo);
132 if (identifier.contains(STREAM_PATH) && identifier.contains(STREAM_ACCESS_PATH_PART)
133 && identifier.contains(STREAM_LOCATION_PATH_PART)) {
134 final String value = (String) node.getValue();
135 final String streamName = value.substring(
136 value.indexOf(CREATE_NOTIFICATION_STREAM.toString() + RestconfConstants.SLASH),
138 this.delegRestconfSubscrService.subscribeToStream(streamName, uriInfo);
141 throw new RestconfDocumentedException(
142 "Request could not be completed because the relevant data model content does not exist",
143 RestconfError.ErrorType.PROTOCOL,
144 RestconfError.ErrorTag.DATA_MISSING);
146 final SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
147 dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
148 final String etag = '"' + node.getNodeType().getModule().getFormattedRevision()
149 + node.getNodeType().getLocalName() + '"';
152 if ((parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL))
153 || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) {
154 resp = Response.status(200)
155 .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
156 .header("ETag", etag)
157 .header("Last-Modified", dateFormatGmt.format(new Date()))
160 resp = Response.status(200)
161 .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
169 public Response putData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
170 Preconditions.checkNotNull(payload);
172 boolean insert_used = false;
173 boolean point_used = false;
174 String insert = null;
177 for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
178 switch (entry.getKey()) {
182 insert = entry.getValue().iterator().next();
184 throw new RestconfDocumentedException("Insert parameter can be used only once.");
190 point = entry.getValue().iterator().next();
192 throw new RestconfDocumentedException("Point parameter can be used only once.");
196 throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
200 checkQueryParams(insert_used, point_used, insert);
202 final InstanceIdentifierContext<? extends SchemaNode> iid = payload
203 .getInstanceIdentifierContext();
205 PutDataTransactionUtil.validInputData(iid.getSchemaNode(), payload);
206 PutDataTransactionUtil.validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
207 PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
209 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
210 final DOMTransactionChain transactionChain;
211 final SchemaContextRef ref;
212 if (mountPoint == null) {
213 transactionChain = this.transactionChainHandler.get();
214 ref = new SchemaContextRef(this.schemaContextHandler.get());
216 transactionChain = transactionChainOfMountPoint(mountPoint);
217 ref = new SchemaContextRef(mountPoint.getSchemaContext());
220 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
221 payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
222 return PutDataTransactionUtil.putData(payload, ref, transactionNode, insert, point);
225 private static void checkQueryParams(final boolean insert_used, final boolean point_used, final String insert) {
226 if (point_used && !insert_used) {
227 throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.");
229 if (point_used && (insert.equals("first") || insert.equals("last"))) {
230 throw new RestconfDocumentedException(
231 "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
236 public Response postData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
237 return postData(payload, uriInfo);
241 public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
242 Preconditions.checkNotNull(payload);
244 boolean insert_used = false;
245 boolean point_used = false;
246 String insert = null;
249 for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
250 switch (entry.getKey()) {
254 insert = entry.getValue().iterator().next();
256 throw new RestconfDocumentedException("Insert parameter can be used only once.");
262 point = entry.getValue().iterator().next();
264 throw new RestconfDocumentedException("Point parameter can be used only once.");
268 throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
272 checkQueryParams(insert_used, point_used, insert);
274 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
275 final DOMTransactionChain transactionChain;
276 final SchemaContextRef ref;
277 if (mountPoint == null) {
278 transactionChain = this.transactionChainHandler.get();
279 ref = new SchemaContextRef(this.schemaContextHandler.get());
281 transactionChain = transactionChainOfMountPoint(mountPoint);
282 ref = new SchemaContextRef(mountPoint.getSchemaContext());
284 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
285 payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
286 return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, ref, insert, point);
290 public Response deleteData(final String identifier) {
291 final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
292 final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
293 identifier, schemaContextRef.get(), Optional.of(this.mountPointServiceHandler.get()));
295 final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
296 final DOMTransactionChain transactionChain;
297 if (mountPoint == null) {
298 transactionChain = this.transactionChainHandler.get();
300 transactionChain = transactionChainOfMountPoint(mountPoint);
303 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
305 return DeleteDataTransactionUtil.deleteData(transactionNode);
309 public PATCHStatusContext patchData(final String identifier, final PATCHContext context, final UriInfo uriInfo) {
310 return patchData(context, uriInfo);
314 public PATCHStatusContext patchData(final PATCHContext context, final UriInfo uriInfo) {
315 Preconditions.checkNotNull(context);
316 final DOMMountPoint mountPoint = context.getInstanceIdentifierContext().getMountPoint();
318 final DOMTransactionChain transactionChain;
319 final SchemaContextRef ref;
320 if (mountPoint == null) {
321 transactionChain = this.transactionChainHandler.get();
322 ref = new SchemaContextRef(this.schemaContextHandler.get());
324 transactionChain = transactionChainOfMountPoint(mountPoint);
325 ref = new SchemaContextRef(mountPoint.getSchemaContext());
328 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
329 context.getInstanceIdentifierContext(), mountPoint, transactionChain);
331 return PatchDataTransactionUtil.patchData(context, transactionNode, ref);
335 * Prepare transaction chain to access data of mount point
337 * - mount point reference
338 * @return {@link DOMTransactionChain}
340 private static DOMTransactionChain transactionChainOfMountPoint(@Nonnull final DOMMountPoint mountPoint) {
341 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
342 if (domDataBrokerService.isPresent()) {
343 return domDataBrokerService.get().createTransactionChain(RestConnectorProvider.transactionListener);
345 final String errMsg = "DOM data broker service isn't available for mount point "
346 + mountPoint.getIdentifier();
348 throw new RestconfDocumentedException(errMsg);