<bundle>mvn:org.opendaylight.controller/sal-rest-connector/${project.version}</bundle>
<bundle>mvn:com.google.code.gson/gson/${gson.version}</bundle>
<bundle>mvn:org.opendaylight.yangtools/yang-data-codec-gson/${yangtools.version}</bundle>
+ <bundle>mvn:org.opendaylight.yangtools/yang-model-export/${yangtools.version}</bundle>
<bundle>mvn:com.sun.jersey/jersey-core/${jersey.version}</bundle>
<bundle>mvn:com.sun.jersey/jersey-server/${jersey.version}</bundle>
<bundle>mvn:com.sun.jersey/jersey-servlet/${jersey.version}</bundle>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-data-codec-gson</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-model-export</artifactId>
+ <!-- FIXME: remove explicit version, once model export package is part of yangtools-artefacts -->
+ <version>0.7.0-SNAPSHOT</version>
+ </dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<Private-Package>org.opendaylight.controller.sal.rest.*,
org.opendaylight.controller.sal.restconf.rpc.*,
org.opendaylight.controller.sal.restconf.impl,
+ org.opendaylight.controller.md.sal.rest.common.*,
org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.rest.connector.rev140724.*,
</Private-Package>
<Import-Package>*,
--- /dev/null
+/**
+ * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.md.sal.rest.common;
+
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+
+/**
+ * sal-rest-connector
+ * org.opendaylight.controller.md.sal.rest.common
+ *
+ * Utility class is centralizing all needed validation functionality for a Restconf osgi module.
+ * All methods have to throw {@link RestconfDocumentedException} only, which is a representation
+ * for all error situation followed by restconf-netconf specification.
+ * @see {@link https://tools.ietf.org/html/draft-bierman-netconf-restconf-02}
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ * Created: Feb 24, 2015
+ */
+public class RestconfValidationUtils {
+
+ private RestconfValidationUtils () {
+ throw new UnsupportedOperationException("Utility class");
+ }
+
+ /**
+ * Method returns {@link RestconfDocumentedException} for a false condition.
+ *
+ * @param condition - condition for rise {@link RestconfDocumentedException}
+ * @param type - input {@link ErrorType} for create {@link RestconfDocumentedException}
+ * @param tag - input {@link ErrorTag} for create {@link RestconfDocumentedException}
+ * @param message - input error message for create {@link RestconfDocumentedException}
+ */
+ public static void checkDocumentedError(final boolean condition, final ErrorType type,
+ final ErrorTag tag, final String message) {
+ if(!condition) {
+ throw new RestconfDocumentedException(message, type, tag);
+ }
+ }
+
+ /**
+ * Method returns {@link RestconfDocumentedException} if value is NULL or same input value.
+ * {@link ErrorType} is relevant for server application layer
+ * {@link ErrorTag} is 404 data-missing
+ * @see {@link https://tools.ietf.org/html/draft-bierman-netconf-restconf-02}
+ *
+ * @param value - some value from {@link org.opendaylight.yangtools.yang.model.api.Module}
+ * @param moduleName - name of {@link org.opendaylight.yangtools.yang.model.api.Module}
+ * @return - T value (same input value)
+ */
+ public static <T> T checkNotNullDocumented(final T value, final String moduleName) {
+ if(value == null) {
+ final String errMsg = "Module " + moduleName + "was not found.";
+ throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
+ }
+ return value;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.rest.schema;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
+
+@Provider
+@Produces(SchemaRetrievalService.YANG_MEDIA_TYPE)
+public class SchemaExportContentYangBodyWriter implements MessageBodyWriter<SchemaExportContext> {
+
+ @Override
+ public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+ final MediaType mediaType) {
+ return type.equals(SchemaExportContext.class);
+ }
+
+ @Override
+ public long getSize(final SchemaExportContext t, final Class<?> type, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public void writeTo(final SchemaExportContext t, final Class<?> type, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType,
+ final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException,
+ WebApplicationException {
+ final PrintWriter writer = new PrintWriter(entityStream);
+ writer.write(t.getModule().getSource());
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.rest.schema;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
+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 javax.xml.stream.XMLStreamException;
+import org.opendaylight.yangtools.yang.model.export.YinExportUtils;
+
+@Provider
+@Produces(SchemaRetrievalService.YIN_MEDIA_TYPE)
+public class SchemaExportContentYinBodyWriter implements MessageBodyWriter<SchemaExportContext> {
+
+ @Override
+ public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations,
+ final MediaType mediaType) {
+ return type.equals(SchemaExportContext.class);
+ }
+
+ @Override
+ public long getSize(final SchemaExportContext t, final Class<?> type, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType) {
+ return -1;
+ }
+
+ @Override
+ public void writeTo(final SchemaExportContext t, final Class<?> type, final Type genericType,
+ final Annotation[] annotations, final MediaType mediaType,
+ final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException,
+ WebApplicationException {
+ try {
+ YinExportUtils.writeModuleToOutputStream(t.getSchemaContext(), t.getModule(), entityStream);
+ } catch (final XMLStreamException e) {
+ throw new IllegalStateException(e);
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.rest.schema;
+
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class SchemaExportContext {
+
+ private final SchemaContext schemaContext;
+ private final Module module;
+
+ public SchemaExportContext(final SchemaContext ctx, final Module module) {
+ schemaContext = ctx;
+ this.module = module;
+ }
+
+ public SchemaContext getSchemaContext() {
+ return schemaContext;
+ }
+
+ public Module getModule() {
+ return module;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.rest.schema;
+
+import com.google.common.annotations.Beta;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+
+@Beta
+public interface SchemaRetrievalService {
+
+ public static final String YANG_MEDIA_TYPE = "application/yang";
+ public static final String YIN_MEDIA_TYPE = "application/yin+xml";
+
+ @GET
+ @Produces({YIN_MEDIA_TYPE,YANG_MEDIA_TYPE})
+ @Path("/modules/module/{identifier:.+}/schema")
+ SchemaExportContext getSchema(@PathParam("identifier") String mountAndModuleId);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.md.sal.rest.schema;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Iterator;
+import org.opendaylight.controller.md.sal.rest.common.RestconfValidationUtils;
+import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class SchemaRetrievalServiceImpl implements SchemaRetrievalService {
+
+ private final ControllerContext salContext;
+
+ private static final Splitter SLASH_SPLITTER = Splitter.on("/");
+ private static final Splitter AT_SPLITTER = Splitter.on("@");
+ private static final String MOUNT_ARG = ControllerContext.MOUNT;
+
+ public SchemaRetrievalServiceImpl(final ControllerContext controllerContext) {
+ salContext = controllerContext;
+ }
+
+
+ @Override
+ public SchemaExportContext getSchema(final String mountAndModule) {
+ final SchemaContext schemaContext;
+ final Iterable<String> pathComponents = SLASH_SPLITTER.split(mountAndModule);
+ final Iterator<String> componentIter = pathComponents.iterator();
+ if(!Iterables.contains(pathComponents, MOUNT_ARG)) {
+ schemaContext = salContext.getGlobalSchema();
+ } else {
+ final StringBuilder pathBuilder = new StringBuilder();
+ while(componentIter.hasNext()) {
+ final String current = componentIter.next();
+ // It is argument, not last element.
+ if(pathBuilder.length() != 0) {
+ pathBuilder.append("/");
+ }
+ pathBuilder.append(current);
+ if(MOUNT_ARG.equals(current)) {
+ // We stop right at mountpoint, last two arguments should
+ // be module name and revision
+ break;
+ }
+ }
+ schemaContext = getMountSchemaContext(pathBuilder.toString());
+
+ }
+
+ RestconfValidationUtils.checkDocumentedError(componentIter.hasNext(),
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Module name must be supplied.");
+ final String moduleName = componentIter.next();
+ RestconfValidationUtils.checkDocumentedError(componentIter.hasNext(),
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Revision date must be supplied.");
+ final String revisionString = componentIter.next();
+ return getExportUsingNameAndRevision(schemaContext, moduleName, revisionString);
+ }
+
+ private SchemaExportContext getExportUsingNameAndRevision(final SchemaContext schemaContext, final String moduleName,
+ final String revisionStr) {
+ try {
+ final Date revision = SimpleDateFormatUtil.getRevisionFormat().parse(revisionStr);
+ final Module module = schemaContext.findModuleByName(moduleName, revision);
+ return new SchemaExportContext(schemaContext, RestconfValidationUtils.checkNotNullDocumented(module, moduleName));
+ } catch (final ParseException e) {
+ throw new RestconfDocumentedException("Supplied revision is not in expected date format YYYY-mm-dd", e);
+ }
+ }
+
+ private SchemaContext getMountSchemaContext(final String identifier) {
+ final InstanceIdentifierContext mountContext = salContext.toMountPointIdentifier(identifier);
+ return mountContext.getSchemaContext();
+ }
+
+
+
+}
+
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
+import org.opendaylight.controller.md.sal.rest.schema.SchemaExportContentYangBodyWriter;
+import org.opendaylight.controller.md.sal.rest.schema.SchemaExportContentYinBodyWriter;
+import org.opendaylight.controller.md.sal.rest.schema.SchemaRetrievalServiceImpl;
import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
.add(JsonNormalizedNodeBodyReader.class)
.add(NormalizedNodeJsonBodyWriter.class)
.add(NormalizedNodeXmlBodyWriter.class)
+ .add(SchemaExportContentYinBodyWriter.class)
+ .add(SchemaExportContentYangBodyWriter.class)
.build();
}
@Override
public Set<Object> getSingletons() {
- Set<Object> singletons = new HashSet<>();
- ControllerContext controllerContext = ControllerContext.getInstance();
- BrokerFacade brokerFacade = BrokerFacade.getInstance();
- RestconfImpl restconfImpl = RestconfImpl.getInstance();
+ final Set<Object> singletons = new HashSet<>();
+ final ControllerContext controllerContext = ControllerContext.getInstance();
+ final BrokerFacade brokerFacade = BrokerFacade.getInstance();
+ final RestconfImpl restconfImpl = RestconfImpl.getInstance();
+ final SchemaRetrievalServiceImpl schemaRetrieval = new SchemaRetrievalServiceImpl(controllerContext);
restconfImpl.setBroker(brokerFacade);
restconfImpl.setControllerContext(controllerContext);
singletons.add(controllerContext);
singletons.add(brokerFacade);
- singletons.add(StatisticsRestconfServiceWrapper.getInstance());
+ singletons.add(schemaRetrieval);
+ singletons.add(new RestconfCompositeWrapper(StatisticsRestconfServiceWrapper.getInstance(), schemaRetrieval));
singletons.add(StructuredDataToXmlProvider.INSTANCE);
singletons.add(StructuredDataToJsonProvider.INSTANCE);
singletons.add(JsonToCompositeNodeProvider.INSTANCE);
--- /dev/null
+package org.opendaylight.controller.sal.rest.impl;
+
+import com.google.common.base.Preconditions;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import org.opendaylight.controller.md.sal.rest.schema.SchemaExportContext;
+import org.opendaylight.controller.md.sal.rest.schema.SchemaRetrievalService;
+import org.opendaylight.controller.sal.rest.api.RestconfService;
+import org.opendaylight.controller.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.controller.sal.restconf.impl.StructuredData;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+
+public class RestconfCompositeWrapper implements RestconfService, SchemaRetrievalService {
+
+ private final RestconfService restconf;
+ private final SchemaRetrievalService schema;
+
+ public RestconfCompositeWrapper(final RestconfService restconf, final SchemaRetrievalService schema) {
+ this.restconf = Preconditions.checkNotNull(restconf);
+ this.schema = Preconditions.checkNotNull(schema);
+ }
+
+ @Override
+ public Object getRoot() {
+ return restconf.getRoot();
+ }
+
+ @Override
+ public StructuredData getModules(final UriInfo uriInfo) {
+ return restconf.getModules(uriInfo);
+ }
+
+ @Override
+ public StructuredData getModules(final String identifier, final UriInfo uriInfo) {
+ return restconf.getModules(identifier, uriInfo);
+ }
+
+ @Override
+ public StructuredData getModule(final String identifier, final UriInfo uriInfo) {
+ return restconf.getModule(identifier, uriInfo);
+ }
+
+ @Override
+ public StructuredData getOperations(final UriInfo uriInfo) {
+ return restconf.getOperations(uriInfo);
+ }
+
+ @Override
+ public StructuredData getOperations(final String identifier, final UriInfo uriInfo) {
+ return restconf.getOperations(identifier, uriInfo);
+ }
+
+ @Override
+ public StructuredData invokeRpc(final String identifier, final CompositeNode payload, final UriInfo uriInfo) {
+ return restconf.invokeRpc(identifier, payload, uriInfo);
+ }
+
+ @Override
+ public StructuredData invokeRpc(final String identifier, final String noPayload, final UriInfo uriInfo) {
+ return restconf.invokeRpc(identifier, noPayload, uriInfo);
+ }
+
+ @Override
+ public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) {
+ return restconf.readConfigurationData(identifier, uriInfo);
+ }
+
+ @Override
+ public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo uriInfo) {
+ return restconf.readOperationalData(identifier, uriInfo);
+ }
+
+ @Override
+ public Response updateConfigurationData(final String identifier, final Node<?> payload) {
+ return restconf.updateConfigurationData(identifier, payload);
+ }
+
+ @Override
+ public Response createConfigurationData(final String identifier, final Node<?> payload) {
+ return restconf.createConfigurationData(identifier, payload);
+ }
+
+ @Override
+ public Response createConfigurationData(final Node<?> payload) {
+ return restconf.createConfigurationData(payload);
+ }
+
+ @Override
+ public Response deleteConfigurationData(final String identifier) {
+ return restconf.deleteConfigurationData(identifier);
+ }
+
+ @Override
+ public Response subscribeToStream(final String identifier, final UriInfo uriInfo) {
+ return restconf.subscribeToStream(identifier, uriInfo);
+ }
+
+ @Override
+ public StructuredData getAvailableStreams(final UriInfo uriInfo) {
+ return restconf.getAvailableStreams(uriInfo);
+ }
+
+ @Override
+ public SchemaExportContext getSchema(final String mountId) {
+ return schema.getSchema(mountId);
+ }
+}