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 com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import java.text.SimpleDateFormat;
13 import java.util.Date;
14 import java.util.List;
15 import java.util.Map.Entry;
16 import java.util.TimeZone;
17 import javax.annotation.Nonnull;
18 import javax.ws.rs.core.Response;
19 import javax.ws.rs.core.UriInfo;
20 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
21 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
22 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
23 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
24 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
25 import org.opendaylight.netconf.sal.restconf.impl.PATCHContext;
26 import org.opendaylight.netconf.sal.restconf.impl.PATCHStatusContext;
27 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
28 import org.opendaylight.netconf.sal.restconf.impl.RestconfError;
29 import org.opendaylight.netconf.sal.restconf.impl.WriterParameters;
30 import org.opendaylight.restconf.RestConnectorProvider;
31 import org.opendaylight.restconf.common.references.SchemaContextRef;
32 import org.opendaylight.restconf.handlers.DOMMountPointServiceHandler;
33 import org.opendaylight.restconf.handlers.SchemaContextHandler;
34 import org.opendaylight.restconf.handlers.TransactionChainHandler;
35 import org.opendaylight.restconf.restful.services.api.RestconfDataService;
36 import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper;
37 import org.opendaylight.restconf.restful.utils.DeleteDataTransactionUtil;
38 import org.opendaylight.restconf.restful.utils.PatchDataTransactionUtil;
39 import org.opendaylight.restconf.restful.utils.PostDataTransactionUtil;
40 import org.opendaylight.restconf.restful.utils.PutDataTransactionUtil;
41 import org.opendaylight.restconf.restful.utils.ReadDataTransactionUtil;
42 import org.opendaylight.restconf.restful.utils.RestconfDataServiceConstant;
43 import org.opendaylight.restconf.utils.parser.ParserIdentifier;
44 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
45 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
50 * Implementation of {@link RestconfDataService}
52 public class RestconfDataServiceImpl implements RestconfDataService {
54 private final static Logger LOG = LoggerFactory.getLogger(RestconfDataServiceImpl.class);
56 private final SchemaContextHandler schemaContextHandler;
57 private final TransactionChainHandler transactionChainHandler;
58 private final DOMMountPointServiceHandler mountPointServiceHandler;
60 public RestconfDataServiceImpl(final SchemaContextHandler schemaContextHandler,
61 final TransactionChainHandler transactionChainHandler,
62 final DOMMountPointServiceHandler mountPointServiceHandler) {
63 this.schemaContextHandler = schemaContextHandler;
64 this.transactionChainHandler = transactionChainHandler;
65 this.mountPointServiceHandler = mountPointServiceHandler;
69 public Response readData(final UriInfo uriInfo) {
70 return readData(null, uriInfo);
74 public Response readData(final String identifier, final UriInfo uriInfo) {
75 final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
76 final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
77 identifier, schemaContextRef.get(), Optional.of(this.mountPointServiceHandler.get()));
79 final WriterParameters parameters = ReadDataTransactionUtil.parseUriParameters(
80 instanceIdentifier, uriInfo);
82 final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
83 final DOMTransactionChain transactionChain;
84 if (mountPoint == null) {
85 transactionChain = this.transactionChainHandler.get();
87 transactionChain = transactionChainOfMountPoint(mountPoint);
90 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
91 instanceIdentifier, mountPoint, transactionChain);
92 final NormalizedNode<?, ?> node = ReadDataTransactionUtil.readData(parameters.getContent(), transactionNode);
94 throw new RestconfDocumentedException(
95 "Request could not be completed because the relevant data model content does not exist",
96 RestconfError.ErrorType.PROTOCOL,
97 RestconfError.ErrorTag.DATA_MISSING);
99 final SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
100 dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
101 final String etag = '"' + node.getNodeType().getModule().getFormattedRevision()
102 + node.getNodeType().getLocalName() + '"';
105 if ((parameters.getContent().equals(RestconfDataServiceConstant.ReadData.ALL))
106 || parameters.getContent().equals(RestconfDataServiceConstant.ReadData.CONFIG)) {
107 resp = Response.status(200)
108 .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
109 .header("ETag", etag)
110 .header("Last-Modified", dateFormatGmt.format(new Date()))
113 resp = Response.status(200)
114 .entity(new NormalizedNodeContext(instanceIdentifier, node, parameters))
122 public Response putData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
123 Preconditions.checkNotNull(payload);
125 boolean insert_used = false;
126 boolean point_used = false;
127 String insert = null;
130 for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
131 switch (entry.getKey()) {
135 insert = entry.getValue().iterator().next();
137 throw new RestconfDocumentedException("Insert parameter can be used only once.");
143 point = entry.getValue().iterator().next();
145 throw new RestconfDocumentedException("Point parameter can be used only once.");
149 throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
153 checkQueryParams(insert_used, point_used, insert);
155 final InstanceIdentifierContext<? extends SchemaNode> iid = payload
156 .getInstanceIdentifierContext();
158 PutDataTransactionUtil.validInputData(iid.getSchemaNode(), payload);
159 PutDataTransactionUtil.validTopLevelNodeName(iid.getInstanceIdentifier(), payload);
160 PutDataTransactionUtil.validateListKeysEqualityInPayloadAndUri(payload);
162 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
163 final DOMTransactionChain transactionChain;
164 final SchemaContextRef ref;
165 if (mountPoint == null) {
166 transactionChain = this.transactionChainHandler.get();
167 ref = new SchemaContextRef(this.schemaContextHandler.get());
169 transactionChain = transactionChainOfMountPoint(mountPoint);
170 ref = new SchemaContextRef(mountPoint.getSchemaContext());
173 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
174 payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
175 return PutDataTransactionUtil.putData(payload, ref, transactionNode, insert, point);
178 private void checkQueryParams(final boolean insert_used, final boolean point_used, final String insert) {
179 if (point_used && !insert_used) {
180 throw new RestconfDocumentedException("Point parameter can't be used without Insert parameter.");
182 if (point_used && (insert.equals("first") || insert.equals("last"))) {
183 throw new RestconfDocumentedException(
184 "Point parameter can be used only with 'after' or 'before' values of Insert parameter.");
189 public Response postData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
190 return postData(payload, uriInfo);
194 public Response postData(final NormalizedNodeContext payload, final UriInfo uriInfo) {
195 Preconditions.checkNotNull(payload);
197 boolean insert_used = false;
198 boolean point_used = false;
199 String insert = null;
202 for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
203 switch (entry.getKey()) {
207 insert = entry.getValue().iterator().next();
209 throw new RestconfDocumentedException("Insert parameter can be used only once.");
215 point = entry.getValue().iterator().next();
217 throw new RestconfDocumentedException("Point parameter can be used only once.");
221 throw new RestconfDocumentedException("Bad parameter for post: " + entry.getKey());
225 checkQueryParams(insert_used, point_used, insert);
227 final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
228 final DOMTransactionChain transactionChain;
229 final SchemaContextRef ref;
230 if (mountPoint == null) {
231 transactionChain = this.transactionChainHandler.get();
232 ref = new SchemaContextRef(this.schemaContextHandler.get());
234 transactionChain = transactionChainOfMountPoint(mountPoint);
235 ref = new SchemaContextRef(mountPoint.getSchemaContext());
237 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
238 payload.getInstanceIdentifierContext(), mountPoint, transactionChain);
239 return PostDataTransactionUtil.postData(uriInfo, payload, transactionNode, ref, insert, point);
243 public Response deleteData(final String identifier) {
244 final SchemaContextRef schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
245 final InstanceIdentifierContext<?> instanceIdentifier = ParserIdentifier.toInstanceIdentifier(
246 identifier, schemaContextRef.get(), Optional.of(this.mountPointServiceHandler.get()));
248 final DOMMountPoint mountPoint = instanceIdentifier.getMountPoint();
249 final DOMTransactionChain transactionChain;
250 if (mountPoint == null) {
251 transactionChain = this.transactionChainHandler.get();
253 transactionChain = transactionChainOfMountPoint(mountPoint);
256 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(instanceIdentifier, mountPoint,
258 return DeleteDataTransactionUtil.deleteData(transactionNode);
262 public PATCHStatusContext patchData(final String identifier, final PATCHContext context, final UriInfo uriInfo) {
263 return patchData(context, uriInfo);
267 public PATCHStatusContext patchData(final PATCHContext context, final UriInfo uriInfo) {
268 Preconditions.checkNotNull(context);
269 final DOMMountPoint mountPoint = context.getInstanceIdentifierContext().getMountPoint();
271 final DOMTransactionChain transactionChain;
272 final SchemaContextRef ref;
273 if (mountPoint == null) {
274 transactionChain = this.transactionChainHandler.get();
275 ref = new SchemaContextRef(this.schemaContextHandler.get());
277 transactionChain = transactionChainOfMountPoint(mountPoint);
278 ref = new SchemaContextRef(mountPoint.getSchemaContext());
281 final TransactionVarsWrapper transactionNode = new TransactionVarsWrapper(
282 context.getInstanceIdentifierContext(), mountPoint, transactionChain);
284 return PatchDataTransactionUtil.patchData(context, transactionNode, ref);
288 * Prepare transaction chain to access data of mount point
290 * - mount point reference
291 * @return {@link DOMTransactionChain}
293 private static DOMTransactionChain transactionChainOfMountPoint(@Nonnull final DOMMountPoint mountPoint) {
294 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
295 if (domDataBrokerService.isPresent()) {
296 return domDataBrokerService.get().createTransactionChain(RestConnectorProvider.transactionListener);
298 final String errMsg = "DOM data broker service isn't available for mount point "
299 + mountPoint.getIdentifier();
301 throw new RestconfDocumentedException(errMsg);