diff --git a/apps/restconf/api/src/main/java/org/onosproject/restconf/api/MediaTypeRestconf.java b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/MediaTypeRestconf.java new file mode 100644 index 0000000000..48ccf99d5e --- /dev/null +++ b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/MediaTypeRestconf.java @@ -0,0 +1,31 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.restconf.api; + +import javax.ws.rs.core.MediaType; + +/* + * Extension of the REST MediaType with 2 new types from the RESTCONF standard RFC 8040. + * + * Currently (1.14) the XML Media Type is not supported by the RESTCONF server in ONOS + */ +public class MediaTypeRestconf extends MediaType { + public static final String APPLICATION_YANG_DATA_XML = "application/yang-data+xml"; + public static final MediaType APPLICATION_YANG_DATA_XML_TYPE = new MediaType("application", "yang-data+xml"); + public static final String APPLICATION_YANG_DATA_JSON = "application/yang-data+json"; + public static final MediaType APPLICATION_YANG_DATA_JSON_TYPE = new MediaType("application", "yang-data+json"); + +} diff --git a/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfJsonBodyWriter.java b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfJsonBodyWriter.java new file mode 100644 index 0000000000..bf9b42c5ab --- /dev/null +++ b/apps/restconf/api/src/main/java/org/onosproject/restconf/api/RestconfJsonBodyWriter.java @@ -0,0 +1,50 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.restconf.api; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +@Provider +@Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) +public class RestconfJsonBodyWriter implements MessageBodyWriter { + private ObjectMapper mapper = new ObjectMapper(); + + @Override + public boolean isWriteable(Class type, Type genericType, + Annotation[] annotations, MediaType mediaType) { + return type == ObjectNode.class; + } + + @Override + public void writeTo(ObjectNode node, Class type, Type genericType, + Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, + OutputStream entityStream) throws IOException { + mapper.writer().writeValue(entityStream, node); + entityStream.flush(); + } +} diff --git a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfProtocolProxy.java b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfProtocolProxy.java index 18ba6edfa4..890900653c 100644 --- a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfProtocolProxy.java +++ b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfProtocolProxy.java @@ -16,6 +16,7 @@ package org.onosproject.protocol.restconf.server.rpp; import org.onlab.rest.AbstractWebApplication; +import org.onosproject.restconf.api.RestconfJsonBodyWriter; import java.util.Set; @@ -26,6 +27,9 @@ public class RestconfProtocolProxy extends AbstractWebApplication { @Override public Set> getClasses() { - return getClasses(RestconfWebResource.class); + return getClasses( + RestconfWebResource.class, + RestconfJsonBodyWriter.class + ); } } diff --git a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java index fd624c26be..179d4edb80 100644 --- a/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java +++ b/protocols/restconf/server/rpp/src/main/java/org/onosproject/protocol/restconf/server/rpp/RestconfWebResource.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.node.ObjectNode; import org.glassfish.jersey.server.ChunkedOutput; import org.onosproject.rest.AbstractWebResource; +import org.onosproject.restconf.api.MediaTypeRestconf; import org.onosproject.restconf.api.RestconfError; import org.onosproject.restconf.api.RestconfException; import org.onosproject.restconf.api.RestconfRpcOutput; @@ -84,7 +85,7 @@ public class RestconfWebResource extends AbstractWebResource { * @return HTTP response - 200, 404 or 500 */ @GET - @Produces(MediaType.APPLICATION_JSON) + @Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) @Path("data/{identifier : .+}") public Response handleGetRequest(@PathParam("identifier") String uriString) { log.debug("handleGetRequest: {}", uriString); @@ -102,19 +103,24 @@ public class RestconfWebResource extends AbstractWebResource { .errorAppTag("handleGetRequest") .build(); return Response.status(NOT_FOUND) - .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build(); + .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))) + .build(); } - return ok(node).build(); + return Response.ok(node) + .build(); } catch (RestconfException e) { log.error("ERROR: handleGetRequest: {}", e.getMessage()); log.debug("Exception in handleGetRequest:", e); - return Response.status(e.getResponse().getStatus()).entity(e.toRestconfErrorJson()).build(); + return Response.status(e.getResponse().getStatus()) + .entity(e.toRestconfErrorJson()) + .build(); } catch (Exception e) { RestconfError error = RestconfError .builder(RestconfError.ErrorType.APPLICATION, RestconfError.ErrorTag.OPERATION_FAILED) .errorMessage(e.getMessage()).errorAppTag("handlePostRequest").build(); return Response.status(INTERNAL_SERVER_ERROR) - .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))).build(); + .entity(RestconfError.wrapErrorAsJson(Arrays.asList(error))) + .build(); } } @@ -134,7 +140,7 @@ public class RestconfWebResource extends AbstractWebResource { * @return A string data stream over HTTP keep-alive session */ @GET - @Produces(MediaType.APPLICATION_JSON) + @Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) @Path("streams/{streamId}") public ChunkedOutput handleNotificationRegistration(@PathParam("streamId") String streamId, @Context HttpServletRequest request) { @@ -164,8 +170,8 @@ public class RestconfWebResource extends AbstractWebResource { * @return HTTP response */ @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) + @Consumes({MediaTypeRestconf.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON}) + @Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) @Path("data") public Response handlePostDatastore(InputStream stream) { @@ -184,8 +190,8 @@ public class RestconfWebResource extends AbstractWebResource { * @return HTTP response */ @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) + @Consumes({ MediaTypeRestconf.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON }) + @Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) @Path("data/{identifier : .+}") public Response handlePostRequest(@PathParam("identifier") String uriString, InputStream stream) { @@ -231,8 +237,8 @@ public class RestconfWebResource extends AbstractWebResource { * @return RPC output */ @POST - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) + @Consumes({ MediaTypeRestconf.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON }) + @Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) @Path("operations/{rpc : .+}") public Response handleRpcRequest(@PathParam("rpc") String rpcName, InputStream rpcInput, @@ -288,8 +294,8 @@ public class RestconfWebResource extends AbstractWebResource { * @return HTTP response */ @PUT - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) + @Consumes({ MediaTypeRestconf.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON }) + @Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) @Path("data/{identifier : .+}") public Response handlePutRequest(@PathParam("identifier") String uriString, InputStream stream) { @@ -335,7 +341,7 @@ public class RestconfWebResource extends AbstractWebResource { * @return HTTP response */ @DELETE - @Produces(MediaType.APPLICATION_JSON) + @Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) @Path("data/{identifier : .+}") public Response handleDeleteRequest(@PathParam("identifier") String uriString) { log.debug("handleDeleteRequest: {}", uriString); @@ -364,8 +370,8 @@ public class RestconfWebResource extends AbstractWebResource { * @return HTTP response */ @PATCH - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) + @Consumes({ MediaTypeRestconf.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON }) + @Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) @Path("data/{identifier : .+}") public Response handlePatchRequest(@PathParam("identifier") String uriString, InputStream stream) { @@ -412,8 +418,8 @@ public class RestconfWebResource extends AbstractWebResource { * @return HTTP response */ @PATCH - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) + @Consumes({ MediaTypeRestconf.APPLICATION_YANG_DATA_JSON, MediaType.APPLICATION_JSON }) + @Produces(MediaTypeRestconf.APPLICATION_YANG_DATA_JSON) @Path("data") public Response handlePatchDatastore(InputStream stream) { log.debug("handlePatchDatastore");