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.nb.rfc8040.rests.services.impl;
10 import static java.util.Objects.requireNonNull;
12 import java.io.InputStream;
13 import java.time.Clock;
14 import java.time.LocalDateTime;
15 import java.time.format.DateTimeFormatter;
16 import javax.ws.rs.Consumes;
17 import javax.ws.rs.DELETE;
18 import javax.ws.rs.Encoded;
19 import javax.ws.rs.GET;
20 import javax.ws.rs.NotFoundException;
21 import javax.ws.rs.POST;
22 import javax.ws.rs.Path;
23 import javax.ws.rs.PathParam;
24 import javax.ws.rs.Produces;
25 import javax.ws.rs.container.AsyncResponse;
26 import javax.ws.rs.container.Suspended;
27 import javax.ws.rs.core.Context;
28 import javax.ws.rs.core.MediaType;
29 import javax.ws.rs.core.Response;
30 import javax.ws.rs.core.Response.Status;
31 import javax.ws.rs.core.UriInfo;
32 import org.eclipse.jdt.annotation.NonNull;
33 import org.opendaylight.restconf.common.errors.RestconfFuture;
34 import org.opendaylight.restconf.nb.rfc8040.MediaTypes;
35 import org.opendaylight.restconf.nb.rfc8040.ReadDataParams;
36 import org.opendaylight.restconf.nb.rfc8040.databind.JsonOperationInputBody;
37 import org.opendaylight.restconf.nb.rfc8040.databind.OperationInputBody;
38 import org.opendaylight.restconf.nb.rfc8040.databind.XmlOperationInputBody;
39 import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.QueryParams;
40 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
41 import org.opendaylight.restconf.server.api.OperationsContent;
42 import org.opendaylight.restconf.server.api.RestconfServer;
43 import org.opendaylight.restconf.server.spi.OperationOutput;
44 import org.opendaylight.yangtools.yang.common.Empty;
45 import org.opendaylight.yangtools.yang.common.Revision;
48 * Baseline RESTCONF implementation with JAX-RS.
51 public final class RestconfImpl {
52 private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MMM-dd HH:mm:ss");
54 private final RestconfServer server;
56 public RestconfImpl(final RestconfServer server) {
57 this.server = requireNonNull(server);
61 * Delete the target data resource.
63 * @param identifier path to target
64 * @param ar {@link AsyncResponse} which needs to be completed
67 @Path("/data/{identifier:.+}")
68 @SuppressWarnings("checkstyle:abbreviationAsWordInName")
69 public void dataDELETE(@Encoded @PathParam("identifier") final String identifier,
70 @Suspended final AsyncResponse ar) {
71 server.dataDELETE(identifier).addCallback(new JaxRsRestconfCallback<>(ar) {
73 Response transform(final Empty result) {
74 return Response.noContent().build();
80 * Get target data resource from data root.
82 * @param uriInfo URI info
83 * @param ar {@link AsyncResponse} which needs to be completed
88 MediaTypes.APPLICATION_YANG_DATA_JSON,
89 MediaTypes.APPLICATION_YANG_DATA_XML,
90 MediaType.APPLICATION_JSON,
91 MediaType.APPLICATION_XML,
94 public void dataGET(@Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
95 final var readParams = QueryParams.newReadDataParams(uriInfo);
96 completeDataGET(server.dataGET(readParams), readParams, ar);
100 * Get target data resource.
102 * @param identifier path to target
103 * @param uriInfo URI info
104 * @param ar {@link AsyncResponse} which needs to be completed
107 @Path("/data/{identifier:.+}")
109 MediaTypes.APPLICATION_YANG_DATA_JSON,
110 MediaTypes.APPLICATION_YANG_DATA_XML,
111 MediaType.APPLICATION_JSON,
112 MediaType.APPLICATION_XML,
115 public void dataGET(@Encoded @PathParam("identifier") final String identifier,
116 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
117 final var readParams = QueryParams.newReadDataParams(uriInfo);
118 completeDataGET(server.dataGET(identifier, readParams), readParams, ar);
121 private static void completeDataGET(final RestconfFuture<NormalizedNodePayload> future,
122 final ReadDataParams readParams, final AsyncResponse ar) {
123 future.addCallback(new JaxRsRestconfCallback<>(ar) {
125 Response transform(final NormalizedNodePayload result) {
126 return switch (readParams.content()) {
127 case ALL, CONFIG -> {
128 final var type = result.data().name().getNodeType();
129 yield Response.status(Status.OK)
131 // FIXME: is this ETag okay?
132 .header("ETag", '"' + type.getModule().getRevision().map(Revision::toString).orElse(null)
133 + "-" + type.getLocalName() + '"')
134 .header("Last-Modified", FORMATTER.format(LocalDateTime.now(Clock.systemUTC())))
137 case NONCONFIG -> Response.status(Status.OK).entity(result).build();
144 * List RPC and action operations in RFC7951 format.
146 * @return A string containing a JSON document conforming to both RFC8040 and RFC7951.
150 @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
151 public String operationsJsonGET() {
152 return server.operationsGET().toJSON();
156 * Retrieve list of operations and actions supported by the server or device in JSON format.
158 * @param operation path parameter to identify device and/or operation
159 * @return A string containing a JSON document conforming to both RFC8040 and RFC7951.
162 @Path("/operations/{operation:.+}")
163 @Produces({ MediaTypes.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON })
164 public String operationsJsonGET(@PathParam("operation") final String operation) {
165 return operationsGET(operation).toJSON();
169 * List RPC and action operations in RFC8040 XML format.
171 * @return A string containing an XML document conforming to both RFC8040 section 11.3.1 and page 84.
175 @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
176 public String operationsXmlGET() {
177 return server.operationsGET().toXML();
181 * Retrieve list of operations and actions supported by the server or device in XML format.
183 * @param operation path parameter to identify device and/or operation
184 * @return A string containing an XML document conforming to both RFC8040 section 11.3.1 and page 84.
187 @Path("/operations/{operation:.+}")
188 @Produces({ MediaTypes.APPLICATION_YANG_DATA_XML, MediaType.APPLICATION_XML, MediaType.TEXT_XML })
189 public String operationsXmlGET(@PathParam("operation") final String operation) {
190 return operationsGET(operation).toXML();
193 private @NonNull OperationsContent operationsGET(final String operation) {
194 final var content = server.operationsGET(operation);
195 if (content == null) {
196 throw new NotFoundException();
202 * Invoke RPC operation.
204 * @param identifier module name and rpc identifier string for the desired operation
205 * @param body the body of the operation
206 * @param uriInfo URI info
207 * @param ar {@link AsyncResponse} which needs to be completed with a {@link NormalizedNodePayload} output
210 // FIXME: identifier is just a *single* QName
211 @Path("/operations/{identifier:.+}")
213 MediaTypes.APPLICATION_YANG_DATA_XML,
214 MediaType.APPLICATION_XML,
218 MediaTypes.APPLICATION_YANG_DATA_JSON,
219 MediaTypes.APPLICATION_YANG_DATA_XML,
220 MediaType.APPLICATION_JSON,
221 MediaType.APPLICATION_XML,
224 public void operationsXmlPOST(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
225 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
226 try (var xmlBody = new XmlOperationInputBody(body)) {
227 operationsPOST(identifier, uriInfo, ar, xmlBody);
232 * Invoke RPC operation.
234 * @param identifier module name and rpc identifier string for the desired operation
235 * @param body the body of the operation
236 * @param uriInfo URI info
237 * @param ar {@link AsyncResponse} which needs to be completed with a {@link NormalizedNodePayload} output
240 // FIXME: identifier is just a *single* QName
241 @Path("/operations/{identifier:.+}")
243 MediaTypes.APPLICATION_YANG_DATA_JSON,
244 MediaType.APPLICATION_JSON,
247 MediaTypes.APPLICATION_YANG_DATA_JSON,
248 MediaTypes.APPLICATION_YANG_DATA_XML,
249 MediaType.APPLICATION_JSON,
250 MediaType.APPLICATION_XML,
253 public void operationsJsonPOST(@Encoded @PathParam("identifier") final String identifier, final InputStream body,
254 @Context final UriInfo uriInfo, @Suspended final AsyncResponse ar) {
255 try (var jsonBody = new JsonOperationInputBody(body)) {
256 operationsPOST(identifier, uriInfo, ar, jsonBody);
260 private void operationsPOST(final String identifier, final UriInfo uriInfo, final AsyncResponse ar,
261 final OperationInputBody body) {
262 server.operationsPOST(uriInfo.getBaseUri(), identifier, body)
263 .addCallback(new JaxRsRestconfCallback<OperationOutput>(ar) {
265 Response transform(final OperationOutput result) {
266 final var body = result.output();
267 return body == null ? Response.noContent().build()
268 : Response.ok().entity(new NormalizedNodePayload(result.operation(), body)).build();
274 * Get revision of IETF YANG Library module.
276 * @return {@link NormalizedNodePayload}
279 @Path("/yang-library-version")
281 MediaTypes.APPLICATION_YANG_DATA_JSON,
282 MediaTypes.APPLICATION_YANG_DATA_XML,
283 MediaType.APPLICATION_JSON,
284 MediaType.APPLICATION_XML,
287 public NormalizedNodePayload yangLibraryVersionGET() {
288 return server.yangLibraryVersionGET();