<clustering.stub.version>0.4.2-SNAPSHOT</clustering.stub.version>
<clustering.test.version>0.4.2-SNAPSHOT</clustering.test.version>
<commmons.northbound.version>0.4.2-SNAPSHOT</commmons.northbound.version>
- <commons.checkstyle.version>0.0.3-SNAPSHOT</commons.checkstyle.version>
<!-- Third Party Versions -->
+ <commons.catalina>7.0.32.v201211201336</commons.catalina>
+ <commons.catalina.ha>7.0.32.v201211201952</commons.catalina.ha>
+ <commons.catalina.tribes>7.0.32.v201211201952</commons.catalina.tribes>
+ <commons.checkstyle.version>0.0.3-SNAPSHOT</commons.checkstyle.version>
<commons.codec.version>1.7</commons.codec.version>
+ <commons.coyote>7.0.32.v201211201952</commons.coyote>
+ <commons.el>7.0.32.v201211081135</commons.el>
<commons.fileupload.version>1.2.2</commons.fileupload.version>
<commons.httpclient.version>0.1.2-SNAPSHOT</commons.httpclient.version>
<commons.io.version>2.4</commons.io.version>
+ <commons.jasper>7.0.32.v201211201952</commons.jasper>
+ <commons.juli.version>7.0.32.v201211081135</commons.juli.version>
<commons.lang.version>3.1</commons.lang.version>
<commons.logback_settings.version>0.0.2-SNAPSHOT</commons.logback_settings.version>
<commons.net.version>3.0.1</commons.net.version>
<commons.opendaylight.concepts.version>0.5.2-SNAPSHOT</commons.opendaylight.concepts.version>
<commons.opendaylight.version>1.4.2-SNAPSHOT</commons.opendaylight.version>
<commons.parent.version>1.0.2-SNAPSHOT</commons.parent.version>
+ <commons.tomcat.api>7.0.32.v201211081135</commons.tomcat.api>
+ <commons.tomcat.util>7.0.32.v201211201952</commons.tomcat.util>
<compiler.version>2.3.2</compiler.version>
<concepts.version>0.5.2-SNAPSHOT</concepts.version>
<config.version>0.2.5-SNAPSHOT</config.version>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.catalina</artifactId>
- <version>7.0.32.v201211201336</version>
+ <version>${commons.catalina}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.catalina.ha</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.catalina.ha}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.catalina.tribes</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.catalina.tribes}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.coyote</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.coyote}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.el</artifactId>
- <version>7.0.32.v201211081135</version>
+ <version>${commons.el}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.jasper</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.jasper}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.juli.extras</artifactId>
- <version>7.0.32.v201211081135</version>
+ <version>${commons.juli.version}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.tomcat.api</artifactId>
- <version>7.0.32.v201211081135</version>
+ <version>${commons.tomcat.api}</version>
</dependency>
<dependency>
<groupId>orbit</groupId>
<artifactId>org.apache.tomcat.util</artifactId>
- <version>7.0.32.v201211201952</version>
+ <version>${commons.tomcat.util}</version>
</dependency>
<dependency>
<groupId>org.aopalliance</groupId>
OpenType<?> innerCompositeType;
if(isDerivedType(innerTypeBaseType, innerType)) {
- innerCompositeType = getCompositeType(innerTypeBaseType, baseInnerTypeDefinition);
+ innerCompositeType = baseInnerTypeDefinition instanceof UnionTypeDefinition ?
+ getCompositeTypeForUnion(baseInnerTypeDefinition) :
+ getCompositeType(innerTypeBaseType, baseInnerTypeDefinition);
} else {
innerCompositeType = SimpleTypeResolver.getSimpleType(innerType);
}
Map<ConnectionConstants, String> params) {
if (connectionService == null)
return null;
- Node node = connectionService.connect(connectionIdentifier, params);
+ Node node = connectionService.connect(type, connectionIdentifier, params);
AbstractScheme scheme = schemes.get(activeScheme);
if (scheme != null && node != null)
scheme.addNode(node);
</plugins>
</build>
</profile>
+ <profile>
+ <id>docs</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>swagger-ui</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+ </profile>
</profiles>
</project>
<name>runtime-mapping-singleton</name>
</binding-mapping-service>
</module>
+ <module>
+ <type xmlns:prefix="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">prefix:binding-forwarded-data-broker</type>
+ <name>binding-async-data-broker</name>
+ <binding-forwarded-data-broker xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">
+ <dom-async-broker>
+ <type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-broker-osgi-registry</type>
+ <name>dom-broker</name>
+ </dom-async-broker>
+ <binding-mapping-service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">binding:binding-dom-mapping-service</type>
+ <name>runtime-mapping-singleton</name>
+ </binding-mapping-service>
+ </binding-forwarded-data-broker>
+ </module>
</modules>
<services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
<service>
</instance>
</service>
+ <service>
+ <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-async-data-broker</type>
+ <instance>
+ <name>binding-data-broker</name>
+ <provider>/modules/module[type='binding-forwarded-data-broker'][name='binding-async-data-broker']</provider>
+ </instance>
+ </service>
+
<service>
<type xmlns:dom="urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom">dom:dom-async-data-broker</type>
<instance>
@Override
public void onFlowRemoved(final FlowRemoved notification) {
+ // notified upon remove flow rpc successfully invoked
if (notification == null) {
return;
}
@Override
public void onSwitchFlowRemoved(final SwitchFlowRemoved notification) {
- // FIXME: unfinished?
+ // notified upon remove flow message from device arrives
+ if (notification == null) {
+ return;
+ }
+
+ final NodeRef node = notification.getNode();
+ if (node == null) {
+ LOG.debug("Notification {} has not node, ignoring it", notification);
+ return;
+ }
+
+ Node adNode;
+ try {
+ adNode = NodeMapping.toADNode(notification.getNode());
+ } catch (ConstructionException e) {
+ LOG.warn("Failed to construct AD node for {}, ignoring notification", node, e);
+ return;
+ }
+ flowProgrammerPublisher.flowRemoved(adNode, ToSalConversionsUtils.toFlow(notification, adNode));
}
@Override
return nodeConnectorId.getValue();
}
+ public static NodeId toAdNodeId(final NodeConnectorId nodeConnectorId) {
+ NodeId nodeId = null;
+ if (nodeConnectorId != null) {
+ nodeId = new NodeId(nodeConnectorId.getValue().replaceFirst(":[0-9]+$", ""));
+ }
+ return nodeId;
+ }
+
public static NodeConnectorId toControllerNodeConnectorId(final NodeId node) {
return new NodeConnectorId(node.getValue() + ":" + 4294967293L);
}
import org.opendaylight.controller.sal.core.NodeConnector;
import org.opendaylight.controller.sal.flowprogrammer.Flow;
import org.opendaylight.controller.sal.match.Match;
+import org.opendaylight.controller.sal.match.MatchType;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Dscp;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.Address;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4;
import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv6;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.GenericFlowAttributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanPcp;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.MacAddressFilter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.net.InetAddresses;
public class ToSalConversionsUtils {
+ private static final Logger LOG = LoggerFactory.getLogger(ToSalConversionsUtils.class);
+
private ToSalConversionsUtils() {
}
public static Flow toFlow(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow source, Node node) {
final Flow target = new Flow();
+ genericFlowToAdFlow(source, target);
+
+ target.setMatch(toMatch(source.getMatch()));
+
+ List<Action> actions = getAction(source);
+ if (actions != null) {
+ target.setActions(actionFrom(actions, node));
+ }
+
+ return target;
+ }
+
+ /**
+ * @param source notification, missing instructions
+ * @param node corresponding node where the flow change occured
+ * @return ad-sal node, build from given data
+ */
+ public static Flow toFlow(SwitchFlowRemoved source, Node node) {
+ final Flow target = new Flow();
+ genericFlowToAdFlow(source, target);
+
+ target.setMatch(toMatch(source.getMatch()));
+
+ return target;
+ }
+ /**
+ * @param source
+ * @param target
+ */
+ private static void genericFlowToAdFlow(GenericFlowAttributes source,
+ final Flow target) {
Integer hardTimeout = source.getHardTimeout();
if (hardTimeout != null) {
target.setHardTimeout(hardTimeout.shortValue());
if (priority != null) {
target.setPriority(priority.shortValue());
}
-
- target.setMatch(toMatch(source.getMatch()));
-
- List<Action> actions = getAction(source);
- if (actions != null) {
- target.setActions(actionFrom(actions, node));
- }
-
target.setId(source.getCookie().getValue().longValue());
- return target;
}
public static List<Action> getAction(
return nodeConnector;
}
- public static Match toMatch(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match source) {
+ public static Match toMatch(org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match source) {
Match target = new Match();
if (source != null) {
fillFrom(target, source.getVlanMatch());
fillFrom(target, source.getLayer3Match());
fillFrom(target, source.getLayer4Match());
fillFrom(target, source.getIpMatch());
+ fillFrom(target, source.getInPort());
}
return target;
}
+ /**
+ * @param target
+ * @param inPort
+ */
+ private static void fillFrom(Match target, NodeConnectorId inPort) {
+ if (inPort != null) {
+ String inPortValue = inPort.getValue();
+ if (inPortValue != null) {
+ try {
+ target.setField(MatchType.IN_PORT, NodeMapping.toADNodeConnector(inPort,
+ NodeMapping.toAdNodeId(inPort)));
+ } catch (ConstructionException e) {
+ LOG.warn("nodeConnector construction failed", e);
+ }
+ }
+ }
+ }
+
private static void fillFrom(Match target, VlanMatch vlanMatch) {
if (vlanMatch != null) {
VlanId vlanId = vlanMatch.getVlanId();
import org.junit.Test;
import org.opendaylight.controller.sal.compatibility.NodeMapping;
import org.opendaylight.controller.sal.core.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
/**
}
}
+ /**
+ * Test method for
+ * {@link org.opendaylight.controller.sal.compatibility.NodeMapping#toAdNodeId(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId)}
+ * .
+ */
+ @Test
+ public void testToAdNodeId() {
+ NodeId observed;
+ observed = NodeMapping.toAdNodeId(null);
+ Assert.assertNull(observed);
+
+ observed = NodeMapping.toAdNodeId(new NodeConnectorId("MD_SAL|openflow:5:2"));
+ Assert.assertEquals("MD_SAL|openflow:5", observed.getValue());
+ }
+
}
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-public interface BindingDataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, BindingDataChangeListener>{
+public interface BindingDataBroker extends AsyncDataBroker<InstanceIdentifier<?>, DataObject, BindingDataChangeListener>, BindingService {
@Override
BindingDataReadTransaction newReadOnlyTransaction();
--- /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.binding.api;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+
+/**
+ *
+ * Marker interface for MD-SAL services which are available for users of MD-SAL.
+ *
+ * BindingService is marker interface for infrastructure services provided by
+ * the SAL. These services may be session-specific, and wrapped by custom
+ * delegator patterns in order to introduce additional semantics / checks
+ * to the system.
+ *
+ * This interface extends {@link BindingAwareService}, order to be make
+ * new services available via
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext}
+ * and via
+ * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext}
+ *
+ */
+public interface BindingService extends BindingAwareService {
+
+}
org.opendaylight.controller.md.sal.binding.impl,
<!--org.opendaylight.controller.sal.binding.dom.*,-->
org.opendaylight.controller.sal.binding.osgi.*,
- org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.binding.impl.rev131028
+ org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.binding.impl.rev131028.*
</Private-Package>
</instructions>
</configuration>
--- /dev/null
+package org.opendaylight.controller.config.yang.md.sal.binding.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import org.opendaylight.controller.md.sal.binding.impl.ForwardedBindingDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
+import org.osgi.framework.BundleContext;
+
+public class BindingAsyncDataBrokerImplModule extends
+ org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModule implements
+ Provider {
+ private BundleContext bundleContext;
+
+ public BindingAsyncDataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
+ super(identifier, dependencyResolver);
+ }
+
+ public BindingAsyncDataBrokerImplModule(
+ final org.opendaylight.controller.config.api.ModuleIdentifier identifier,
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,
+ final org.opendaylight.controller.config.yang.md.sal.binding.impl.BindingAsyncDataBrokerImplModule oldModule,
+ final java.lang.AutoCloseable oldInstance) {
+ super(identifier, dependencyResolver, oldModule, oldInstance);
+ }
+
+ @Override
+ public void customValidation() {
+ // add custom validation form module attributes here.
+ }
+
+ @Override
+ public java.lang.AutoCloseable createInstance() {
+ Broker domBroker = getDomAsyncBrokerDependency();
+ BindingIndependentMappingService mappingService = getBindingMappingServiceDependency();
+
+ // FIXME: Switch this to DOM Broker registration which would not require
+ // BundleContext when API are updated.
+ ProviderSession session = domBroker.registerProvider(this, getBundleContext());
+ DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class);
+ SchemaService schemaService = session.getService(SchemaService.class);
+ return new ForwardedBindingDataBroker(domDataBroker, mappingService, schemaService);
+ }
+
+ // FIXME: Remove this when DOM Broker registration would not require
+ // BundleContext
+ @Deprecated
+ private BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ // FIXME: Remove this when DOM Broker registration would not require
+ // BundleContext
+ @Deprecated
+ void setBundleContext(final BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ @Override
+ public Collection<ProviderFunctionality> getProviderFunctionality() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void onSessionInitiated(final ProviderSession arg0) {
+ // intentional NOOP
+ }
+
+}
--- /dev/null
+/*
+* Generated file
+*
+* Generated from: yang module name: opendaylight-sal-binding-broker-impl yang module local name: binding-forwarded-data-broker
+* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator
+* Generated at: Fri May 16 17:18:18 CEST 2014
+*
+* Do not modify this file unless it is present under src/main directory
+*/
+package org.opendaylight.controller.config.yang.md.sal.binding.impl;
+
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.osgi.framework.BundleContext;
+
+public class BindingAsyncDataBrokerImplModuleFactory extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModuleFactory {
+
+
+
+
+ @Override
+ public BindingAsyncDataBrokerImplModule instantiateModule(final String instanceName,
+ final DependencyResolver dependencyResolver, final BindingAsyncDataBrokerImplModule oldModule,
+ final AutoCloseable oldInstance, final BundleContext bundleContext) {
+ BindingAsyncDataBrokerImplModule module = super.instantiateModule(instanceName, dependencyResolver, oldModule, oldInstance, bundleContext);
+ module.setBundleContext(bundleContext);
+ return module;
+ }
+
+ @Override
+ public BindingAsyncDataBrokerImplModule instantiateModule(final String instanceName,
+ final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
+ // TODO Auto-generated method stub
+ BindingAsyncDataBrokerImplModule module = super.instantiateModule(instanceName, dependencyResolver, bundleContext);
+ module.setBundleContext(bundleContext);
+ return module;
+ }
+}
\r
private BundleContext bundleContext;\r
\r
- public BindingBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
- org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {\r
+ public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {\r
super(identifier, dependencyResolver);\r
}\r
\r
- public BindingBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
- org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,\r
- BindingBrokerImplModule oldModule, java.lang.AutoCloseable oldInstance) {\r
+ public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier,\r
+ final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver,\r
+ final BindingBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) {\r
super(identifier, dependencyResolver, oldModule, oldInstance);\r
}\r
\r
private RootBindingAwareBroker createStandaloneBroker() {\r
RootBindingAwareBroker broker = new RootBindingAwareBroker(getIdentifier().getInstanceName());\r
\r
- broker.setDataBroker(getDataBrokerDependency());\r
+ broker.setLegacyDataBroker(getDataBrokerDependency());\r
broker.setNotificationBroker(getNotificationServiceDependency());\r
broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier()));\r
+ // FIXME: Also set Async Data Broker\r
return broker;\r
}\r
\r
private RootBindingAwareBroker createForwardedBroker() {\r
DomForwardedBindingBrokerImpl broker = new DomForwardedBindingBrokerImpl(getIdentifier().getInstanceName());\r
\r
- broker.setDataBroker(getDataBrokerDependency());\r
+ broker.setLegacyDataBroker(getDataBrokerDependency());\r
broker.setNotificationBroker(getNotificationServiceDependency());\r
broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier()));\r
\r
broker.getMountManager().setDataCommitExecutor(SingletonHolder.getDefaultCommitExecutor());\r
broker.getMountManager().setNotificationExecutor(SingletonHolder.getDefaultNotificationExecutor());\r
\r
-\r
+ // FIXME: Also set Async Data Broker\r
DomForwardingUtils.reuseForwardingFrom(broker, broker.getDataBroker());\r
broker.startForwarding();\r
return broker;\r
return bundleContext;\r
}\r
\r
- public void setBundleContext(BundleContext bundleContext) {\r
+ public void setBundleContext(final BundleContext bundleContext) {\r
this.bundleContext = bundleContext;\r
}\r
}\r
Broker domBroker = getDomAsyncBrokerDependency();
ProviderSession session = domBroker.registerProvider(this, getBundleContext());
DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class);
+ SchemaService schemaService = session.getService(SchemaService.class);
ForwardedBackwardsCompatibleDataBroker dataBroker = new ForwardedBackwardsCompatibleDataBroker(domDataBroker,
- mappingService, listeningExecutor);
-
- session.getService(SchemaService.class).registerSchemaServiceListener(dataBroker);
+ mappingService, schemaService,listeningExecutor);
dataBroker.setConnector(BindingDomConnectorDeployer.createConnector(getBindingMappingServiceDependency()));
dataBroker.setDomProviderContext(session);
import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector;
import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedBroker;
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.concepts.AbstractListenerRegistration;
import org.opendaylight.yangtools.concepts.Delegator;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
+import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
public abstract class AbstractForwardedDataBroker implements Delegator<DOMDataBroker>, DomForwardedBroker,
- SchemaContextListener {
+ SchemaContextListener, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class);
// The Broker to whom we do all forwarding
private final BindingToNormalizedNodeCodec codec;
private BindingIndependentConnector connector;
private ProviderSession context;
+ private final ListenerRegistration<SchemaServiceListener> schemaListenerRegistration;
protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker,
- final BindingIndependentMappingService mappingService) {
+ final BindingIndependentMappingService mappingService,final SchemaService schemaService) {
this.domDataBroker = domDataBroker;
this.mappingService = mappingService;
this.codec = new BindingToNormalizedNodeCodec(mappingService);
+ this.schemaListenerRegistration = schemaService.registerSchemaServiceListener(this);
}
protected BindingToNormalizedNodeCodec getCodec() {
// NOOP
}
+ @Override
+ public void close() throws Exception {
+ this.schemaListenerRegistration.close();
+ }
+
}
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
import org.opendaylight.yangtools.concepts.Delegator;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
private static final Logger LOG = LoggerFactory.getLogger(ForwardedBackwardsCompatibleDataBroker.class);
private final ConcurrentHashMap<InstanceIdentifier<?>, CommitHandlerRegistrationImpl> commitHandlers = new ConcurrentHashMap<>();
- private final ListenerRegistry<DataChangeListener> fakeRegistry = ListenerRegistry.create();
private final ListeningExecutorService executorService;
public ForwardedBackwardsCompatibleDataBroker(final DOMDataBroker domDataBroker,
- final BindingIndependentMappingService mappingService, final ListeningExecutorService executor) {
- super(domDataBroker, mappingService);
+ final BindingIndependentMappingService mappingService, final SchemaService schemaService,final ListeningExecutorService executor) {
+ super(domDataBroker, mappingService,schemaService);
executorService = executor;
LOG.info("ForwardedBackwardsCompatibleBroker started.");
}
throw new UnsupportedOperationException("Data reader contract is not supported.");
}
- @Override
- public void close() throws Exception {
- // TODO Auto-generated method stub
-
- }
-
public ListenableFuture<RpcResult<TransactionStatus>> commit(final ForwardedBackwardsCompatibleTransacion tx) {
final List<DataCommitTransaction<InstanceIdentifier<? extends DataObject>, DataObject>> subTrans = new ArrayList<>();
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.common.RpcResult;
*/
public class ForwardedBindingDataBroker extends AbstractForwardedDataBroker implements BindingDataBroker {
- public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService) {
- super(domDataBroker, mappingService);
+ public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService, final SchemaService schemaService) {
+ super(domDataBroker, mappingService,schemaService);
}
@Override
import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils
import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.*
+import org.opendaylight.yangtools.yang.binding.RpcService
class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator {
val proxyName = iface.directProxyName;
val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName)
if(potentialClass != null) {
- return potentialClass.newInstance;
+ return potentialClass.newInstance as RpcService;
}
val supertype = iface.asCtClass
val createdCls = createClass(iface.directProxyName, supertype) [
'''
]
]
- return createdCls.toClass(iface.classLoader).newInstance
+ return createdCls.toClass(iface.classLoader).newInstance as RpcService
]
}
val routerName = iface.routerName;
val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName)
if(potentialClass != null) {
- return potentialClass.newInstance;
+ return potentialClass.newInstance as RpcService;
}
val targetCls = createClass(iface.routerName, supertype) [
'''
]
]
- return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance
+ return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as RpcService
];
}
*/
package org.opendaylight.controller.sal.binding.impl;
-import com.google.common.collect.ImmutableClassToInstanceMap;
+import static com.google.common.base.Preconditions.checkState;
+
+import org.opendaylight.controller.md.sal.binding.api.BindingDataBroker;
import org.opendaylight.controller.md.sal.binding.util.AbstractBindingSalProviderInstance;
import org.opendaylight.controller.md.sal.binding.util.BindingContextUtils;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableClassToInstanceMap;
public class RootBindingAwareBroker implements //
Mutable, //
Identifiable<String>, //
- BindingAwareBroker, AutoCloseable,
- RpcProviderRegistry {
+ BindingAwareBroker, AutoCloseable, RpcProviderRegistry {
private final static Logger LOG = LoggerFactory.getLogger(RootBindingAwareBroker.class);
private NotificationProviderService notificationBroker;
- private DataProviderService dataBroker;
+ private DataProviderService legacyDataBroker;
+
+ private BindingDataBroker dataBroker;
private MountPointManagerImpl mountManager;
return mountManager;
}
- public void setMountManager(MountPointManagerImpl mountManager) {
+ public void setMountManager(final MountPointManagerImpl mountManager) {
this.mountManager = mountManager;
}
private ImmutableClassToInstanceMap<BindingAwareService> supportedProviderServices;
- public RootBindingAwareBroker(String instanceName) {
+ public RootBindingAwareBroker(final String instanceName) {
this.identifier = instanceName;
mountManager = new MountPointManagerImpl();
}
+ @Override
public String getIdentifier() {
return identifier;
}
}
public DataProviderService getDataBroker() {
- return this.dataBroker;
+ return this.legacyDataBroker;
}
public NotificationProviderService getNotificationBroker() {
return rpcBroker;
}
- public void setRpcBroker(RpcProviderRegistry rpcBroker) {
+ public void setRpcBroker(final RpcProviderRegistry rpcBroker) {
this.rpcBroker = rpcBroker;
}
- public void setNotificationBroker(NotificationProviderService notificationBroker) {
+ public void setNotificationBroker(final NotificationProviderService notificationBroker) {
this.notificationBroker = notificationBroker;
}
- public void setDataBroker(DataProviderService dataBroker) {
- this.dataBroker = dataBroker;
+ public void setLegacyDataBroker(final DataProviderService dataBroker) {
+ this.legacyDataBroker = dataBroker;
}
public void start() {
controllerRoot = new RootSalInstance(getRpcProviderRegistry(), getNotificationBroker(), getDataBroker());
+ ImmutableClassToInstanceMap.Builder<BindingAwareService> consBuilder = ImmutableClassToInstanceMap.builder();
- supportedConsumerServices = ImmutableClassToInstanceMap.<BindingAwareService> builder()
- .put(NotificationService.class, getRoot()) //
- .put(DataBrokerService.class, getRoot()) //
- .put(RpcConsumerRegistry.class, getRoot()) //
- .put(MountService.class, mountManager).build();
-
+ consBuilder.put(NotificationService.class, getRoot());
+ consBuilder.put(DataBrokerService.class, getRoot());
+ consBuilder.put(RpcConsumerRegistry.class, getRoot());
+ if(dataBroker != null) {
+ consBuilder.put(BindingDataBroker.class, dataBroker);
+ }
+ consBuilder.put(MountService.class, mountManager).build();
+ supportedConsumerServices = consBuilder.build();
supportedProviderServices = ImmutableClassToInstanceMap.<BindingAwareService> builder()
- .putAll(supportedConsumerServices)
- .put(NotificationProviderService.class, getRoot()) //
- .put(DataProviderService.class, getRoot()) //
- .put(RpcProviderRegistry.class, getRoot()) //
+ .putAll(supportedConsumerServices).put(NotificationProviderService.class, getRoot())
+ .put(DataProviderService.class, getRoot()).put(RpcProviderRegistry.class, getRoot())
.put(MountProviderService.class, mountManager).build();
}
@Override
- public ConsumerContext registerConsumer(BindingAwareConsumer consumer, BundleContext ctx) {
+ public ConsumerContext registerConsumer(final BindingAwareConsumer consumer, final BundleContext ctx) {
checkState(supportedConsumerServices != null, "Broker is not initialized.");
return BindingContextUtils.createConsumerContextAndInitialize(consumer, supportedConsumerServices);
}
@Override
- public ProviderContext registerProvider(BindingAwareProvider provider, BundleContext ctx) {
+ public ProviderContext registerProvider(final BindingAwareProvider provider, final BundleContext ctx) {
checkState(supportedProviderServices != null, "Broker is not initialized.");
return BindingContextUtils.createProviderContextAndInitialize(provider, supportedProviderServices);
}
}
@Override
- public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(Class<T> type, T implementation)
- throws IllegalStateException {
+ public <T extends RpcService> RoutedRpcRegistration<T> addRoutedRpcImplementation(final Class<T> type,
+ final T implementation) throws IllegalStateException {
return getRoot().addRoutedRpcImplementation(type, implementation);
}
@Override
- public <T extends RpcService> RpcRegistration<T> addRpcImplementation(Class<T> type, T implementation)
+ public <T extends RpcService> RpcRegistration<T> addRpcImplementation(final Class<T> type, final T implementation)
throws IllegalStateException {
return getRoot().addRpcImplementation(type, implementation);
}
@Override
- public <T extends RpcService> T getRpcService(Class<T> module) {
+ public <T extends RpcService> T getRpcService(final Class<T> module) {
return getRoot().getRpcService(module);
}
+
@Override
public <L extends RouteChangeListener<RpcContextIdentifier, InstanceIdentifier<?>>> ListenerRegistration<L> registerRouteChangeListener(
- L arg0) {
+ final L arg0) {
return getRoot().registerRouteChangeListener(arg0);
}
-
public class RootSalInstance extends
AbstractBindingSalProviderInstance<DataProviderService, NotificationProviderService, RpcProviderRegistry> {
- public RootSalInstance(RpcProviderRegistry rpcRegistry, NotificationProviderService notificationBroker,
- DataProviderService dataBroker) {
+ public RootSalInstance(final RpcProviderRegistry rpcRegistry,
+ final NotificationProviderService notificationBroker, final DataProviderService dataBroker) {
super(rpcRegistry, notificationBroker, dataBroker);
}
}
+
+ public void setDataBroker(final BindingDataBroker asyncDataBroker) {
+ dataBroker = asyncDataBroker;
+ }
}
config:provided-service sal:binding-data-consumer-broker;
config:java-name-prefix ForwardedCompatibleDataBrokerImpl;
}
+
+ identity binding-forwarded-data-broker {
+ base config:module-type;
+ config:provided-service sal:binding-async-data-broker;
+ config:java-name-prefix BindingAsyncDataBrokerImpl;
+ }
identity binding-rpc-broker {
base config:module-type;
config:java-name-prefix RuntimeMapping;
}
+ grouping dom-forwarding-component {
+ container dom-async-broker {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity dom:dom-broker-osgi-registry;
+ }
+ }
+ }
+
+ container binding-mapping-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity binding-dom-mapping-service;
+ }
+ }
+ }
+ }
+
augment "/config:modules/config:module/config:configuration" {
case binding-broker-impl {
when "/config:modules/config:module/config:type = 'binding-broker-impl'";
case binding-data-compatible-broker {
when "/config:modules/config:module/config:type = 'binding-data-compatible-broker'";
- container dom-async-broker {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity dom:dom-broker-osgi-registry;
- }
- }
- }
-
- container binding-mapping-service {
- uses config:service-ref {
- refine type {
- mandatory true;
- config:required-identity binding-dom-mapping-service;
- }
- }
+ uses dom-forwarding-component;
+ }
+ }
+
+ augment "/config:modules/config:module/config:configuration" {
+ case binding-forwarded-data-broker {
+ when "/config:modules/config:module/config:type = 'binding-forwarded-data-broker'";
+ container binding-forwarded-data-broker {
+ uses dom-forwarding-component;
}
}
}
import org.opendaylight.controller.sal.dom.broker.impl.HashMapDataStore;
import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter;
import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker;
-import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListeningExecutorService;
-public class BindingTestContext implements AutoCloseable, SchemaContextProvider {
+public class BindingTestContext implements AutoCloseable {
public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier TREE_ROOT = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
.builder().toInstance();
private MountPointManagerImpl biMountImpl;
- private SchemaContext schemaContext;
+
private ImmutableMap<LogicalDatastoreType, DOMStore> newDatastores;
private BackwardsCompatibleDataBroker biCompatibleBroker;
- private final List<SchemaContextListener> schemaListeners = new ArrayList<>();
-
private DataProviderService baData;
private DOMDataBroker newDOMDataBroker;
- @Override
- public SchemaContext getSchemaContext() {
- return schemaContext;
- }
+ private final MockSchemaService mockSchemaService = new MockSchemaService();
+
+
public DOMDataBroker getDomAsyncDataBroker() {
return newDOMDataBroker;
this.startWithSchema = startWithSchema;
}
+ @Deprecated
public void startDomDataStore() {
checkState(dataStore == null, "DataStore already started.");
checkState(biDataImpl != null, "Dom Data Broker not present");
} else {
dataStore = schemaAwareDataStore;
}
-
+ mockSchemaService.registerSchemaServiceListener(schemaAwareDataStore);
biDataImpl.registerConfigurationReader(TREE_ROOT, dataStore);
biDataImpl.registerOperationalReader(TREE_ROOT, dataStore);
biDataImpl.registerCommitHandler(TREE_ROOT, dataStore);
biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker);
- schemaListeners.add(configStore);
- schemaListeners.add(operStore);
- schemaListeners.add(biCompatibleBroker);
+ mockSchemaService.registerSchemaServiceListener(configStore);
+ mockSchemaService.registerSchemaServiceListener(operStore);
+ mockSchemaService.registerSchemaServiceListener(biCompatibleBroker);
biDataLegacyBroker = biCompatibleBroker;
}
baBrokerImpl.getMountManager().setDataCommitExecutor(executor);
baBrokerImpl.getMountManager().setNotificationExecutor(executor);
baBrokerImpl.setRpcBroker(new RpcProviderRegistryImpl("test"));
- baBrokerImpl.setDataBroker(baData);
+ baBrokerImpl.setLegacyDataBroker(baData);
baBrokerImpl.setNotificationBroker(baNotifyImpl);
baBrokerImpl.start();
}
public void startBindingToDomMappingService() {
checkState(classPool != null, "ClassPool needs to be present");
mappingServiceImpl = new RuntimeGeneratedMappingServiceImpl(classPool);
+ mockSchemaService.registerSchemaServiceListener(mappingServiceImpl);
}
public void updateYangSchema(final String[] files) {
- schemaContext = getContext(files);
-
- if (schemaAwareDataStore != null) {
- schemaAwareDataStore.onGlobalContextUpdated(schemaContext);
- }
- if (mappingServiceImpl != null) {
- mappingServiceImpl.onGlobalContextUpdated(schemaContext);
- }
- for(SchemaContextListener listener : schemaListeners) {
- listener.onGlobalContextUpdated(schemaContext);
- }
+ mockSchemaService.changeSchema(getContext(files));
}
public static String[] getAllYangFilesOnClasspath() {
}
public void startNewBindingDataBroker() {
- ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl, executor);
- schemaListeners.add(forwarded);
+ ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl,mockSchemaService, executor);
baData = forwarded;
}
private void startDomBroker() {
checkState(executor != null);
biBrokerImpl = new BrokerImpl();
- biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", this));
+ biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", mockSchemaService));
}
public MountProvisionService getDomMountProviderService() {
return biMountImpl;
}
+
+
}
--- /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.sal.binding.test.util;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener;
+
+@SuppressWarnings("deprecation")
+public final class MockSchemaService implements SchemaService, SchemaContextProvider {
+
+ private SchemaContext schemaContext;
+
+ ListenerRegistry<SchemaServiceListener> listeners = ListenerRegistry.create();
+
+ @Override
+ public void addModule(final Module module) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized SchemaContext getGlobalContext() {
+ return schemaContext;
+ }
+
+ @Override
+ public synchronized SchemaContext getSessionContext() {
+ return schemaContext;
+ }
+
+ @Override
+ public ListenerRegistration<SchemaServiceListener> registerSchemaServiceListener(
+ final SchemaServiceListener listener) {
+ return listeners.register(listener);
+ }
+
+ @Override
+ public void removeModule(final Module module) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public synchronized SchemaContext getSchemaContext() {
+ return schemaContext;
+ }
+
+ public synchronized void changeSchema(final SchemaContext newContext) {
+ schemaContext = newContext;
+ for (ListenerRegistration<SchemaServiceListener> listener : listeners) {
+ listener.getInstance().onGlobalContextUpdated(schemaContext);
+ }
+ }
+}
\ No newline at end of file
base "config:service-type";
config:java-class "org.opendaylight.controller.sal.binding.api.data.DataProviderService";
}
+
+ identity binding-async-data-broker {
+ base "config:service-type";
+ config:java-class "org.opendaylight.controller.md.sal.binding.api.BindingDataBroker";
+ }
identity binding-data-consumer-broker {
base "config:service-type";
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Iterator;
+import java.util.List;
import java.util.Queue;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.client.NetconfClientDispatcher;
import org.opendaylight.controller.netconf.client.NetconfClientSession;
import org.opendaylight.controller.netconf.client.NetconfClientSessionListener;
import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration;
import org.opendaylight.controller.netconf.util.xml.XmlElement;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import org.opendaylight.controller.sal.common.util.RpcErrors;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class);
- private static final RpcResult<NetconfMessage> FAILED_RPC_RESULT = new FailedRpcResult<>(RpcErrors.getRpcError(
- null, null, null, RpcError.ErrorSeverity.ERROR, "Netconf session disconnected",
- RpcError.ErrorType.PROTOCOL, null));
-
private final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice;
private final RemoteDeviceId id;
+ private final Lock sessionLock = new ReentrantLock();
public NetconfDeviceCommunicator(final RemoteDeviceId id,
final RemoteDevice<NetconfSessionCapabilities, NetconfMessage> remoteDevice) {
private NetconfClientSession session;
@Override
- public synchronized void onSessionUp(final NetconfClientSession session) {
- logger.debug("{}: Session established", id);
- this.session = session;
+ public void onSessionUp(final NetconfClientSession session) {
+ sessionLock.lock();
+ try {
+ logger.debug("{}: Session established", id);
+ this.session = session;
- final NetconfSessionCapabilities netconfSessionCapabilities = NetconfSessionCapabilities.fromNetconfSession(session);
- logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities);
+ final NetconfSessionCapabilities netconfSessionCapabilities =
+ NetconfSessionCapabilities.fromNetconfSession(session);
+ logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities);
- remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this);
+ remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this);
+ }
+ finally {
+ sessionLock.unlock();
+ }
}
public void initializeRemoteConnection(final NetconfClientDispatcher dispatch,
dispatch.createReconnectingClient(config);
}
- private synchronized void tearDown(final Exception e) {
- remoteDevice.onRemoteSessionDown();
- session = null;
+ private void tearDown( String reason ) {
+ List<UncancellableFuture<RpcResult<NetconfMessage>>> futuresToCancel = Lists.newArrayList();
+ sessionLock.lock();
+ try {
+ if( session != null ) {
+ session = null;
+
+ /*
+ * Walk all requests, check if they have been executing
+ * or cancelled and remove them from the queue.
+ */
+ final Iterator<Request> it = requests.iterator();
+ while (it.hasNext()) {
+ final Request r = it.next();
+ if (r.future.isUncancellable()) {
+ futuresToCancel.add( r.future );
+ it.remove();
+ } else if (r.future.isCancelled()) {
+ // This just does some house-cleaning
+ it.remove();
+ }
+ }
- /*
- * Walk all requests, check if they have been executing
- * or cancelled and remove them from the queue.
- */
- final Iterator<Request> it = requests.iterator();
- while (it.hasNext()) {
- final Request r = it.next();
- if (r.future.isUncancellable()) {
- r.future.setException(e);
- it.remove();
- } else if (r.future.isCancelled()) {
- // This just does some house-cleaning
- it.remove();
+ remoteDevice.onRemoteSessionDown();
+ }
+ }
+ finally {
+ sessionLock.unlock();
+ }
+
+ // Notify pending request futures outside of the sessionLock to avoid unnecessarily
+ // blocking the caller.
+ for( UncancellableFuture<RpcResult<NetconfMessage>> future: futuresToCancel ) {
+ if( Strings.isNullOrEmpty( reason ) ) {
+ future.set( createSessionDownRpcResult() );
+ } else {
+ future.set( createErrorRpcResult( RpcError.ErrorType.TRANSPORT, reason ) );
}
}
}
+ private RpcResult<NetconfMessage> createSessionDownRpcResult()
+ {
+ return createErrorRpcResult( RpcError.ErrorType.TRANSPORT,
+ String.format( "The netconf session to %1$s is disconnected", id.getName() ) );
+ }
+
+ private RpcResult<NetconfMessage> createErrorRpcResult( RpcError.ErrorType errorType, String message )
+ {
+ return new FailedRpcResult<NetconfMessage>( RpcErrors.getRpcError( null,
+ NetconfDocumentedException.ErrorTag.operation_failed.getTagValue(),
+ null, RpcError.ErrorSeverity.ERROR, message, errorType, null ) );
+ }
+
@Override
public void onSessionDown(final NetconfClientSession session, final Exception e) {
logger.warn("{}: Session went down", id, e);
- tearDown(e);
+ tearDown( null );
}
@Override
public void onSessionTerminated(final NetconfClientSession session, final NetconfTerminationReason reason) {
logger.warn("{}: Session terminated {}", id, reason);
- tearDown(new RuntimeException(reason.getErrorMessage()));
+ tearDown( reason.getErrorMessage() );
+ }
+
+ @Override
+ public void close() {
+ tearDown( String.format( "The netconf session to %1$s has been closed", id.getName() ) );
}
@Override
}
}
- private synchronized void processMessage(final NetconfMessage message) {
- final Request r = requests.peek();
- if (r.future.isUncancellable()) {
- requests.poll();
+ private void processMessage(final NetconfMessage message) {
+ Request request = null;
+ sessionLock.lock();
+ try {
+ request = requests.peek();
+ if (request.future.isUncancellable()) {
+ requests.poll();
+ }
+ else {
+ request = null;
+ logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message));
+ }
+ }
+ finally {
+ sessionLock.unlock();
+ }
+
+ if( request != null ) {
logger.debug("{}: Message received {}", id, message);
if(logger.isTraceEnabled()) {
- logger.trace("{}: Matched request: {} to response: {}", id, msgToS(r.request), msgToS(message));
+ logger.trace( "{}: Matched request: {} to response: {}", id,
+ msgToS( request.request ), msgToS( message ) );
}
try {
- NetconfMessageTransformUtil.checkValidReply(r.request, message);
- } catch (final IllegalStateException e) {
- logger.warn("{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}", id,
- msgToS(r.request), msgToS(message), e);
- r.future.setException(e);
+ NetconfMessageTransformUtil.checkValidReply( request.request, message );
+ }
+ catch (final NetconfDocumentedException e) {
+ logger.warn( "{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}",
+ id, msgToS( request.request ), msgToS( message ), e );
+
+ request.future.set( new FailedRpcResult<NetconfMessage>(
+ NetconfMessageTransformUtil.toRpcError( e ) ) );
return;
}
try {
NetconfMessageTransformUtil.checkSuccessReply(message);
- } catch (NetconfDocumentedException | IllegalStateException e) {
- logger.warn("{}: Error reply from remote device, request: {}, response: {}", id,
- msgToS(r.request), msgToS(message), e);
- r.future.setException(e);
+ }
+ catch( NetconfDocumentedException e ) {
+ logger.warn( "{}: Error reply from remote device, request: {}, response: {}", id,
+ msgToS( request.request ), msgToS( message ), e );
+
+ request.future.set( new FailedRpcResult<NetconfMessage>(
+ NetconfMessageTransformUtil.toRpcError( e ) ) );
return;
}
- r.future.set(Rpcs.getRpcResult(true, message, Collections.<RpcError>emptySet()));
- } else {
- logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message));
+ request.future.set(Rpcs.getRpcResult( true, message, Collections.<RpcError>emptySet() ) );
}
}
- @Override
- public void close() {
- tearDown(new RuntimeException("Closed"));
- }
-
private static String msgToS(final NetconfMessage msg) {
return XmlUtil.toString(msg.getDocument());
}
@Override
- public synchronized ListenableFuture<RpcResult<NetconfMessage>> sendRequest(final NetconfMessage message, final QName rpc) {
+ public ListenableFuture<RpcResult<NetconfMessage>> sendRequest(
+ final NetconfMessage message, final QName rpc) {
+ sessionLock.lock();
+ try {
+ return sendRequestWithLock( message, rpc );
+ }
+ finally {
+ sessionLock.unlock();
+ }
+ }
+
+ private ListenableFuture<RpcResult<NetconfMessage>> sendRequestWithLock(
+ final NetconfMessage message, final QName rpc) {
if(logger.isTraceEnabled()) {
logger.trace("{}: Sending message {}", id, msgToS(message));
}
if (session == null) {
logger.warn("{}: Session is disconnected, failing RPC request {}", id, message);
- return Futures.immediateFuture(FAILED_RPC_RESULT);
+ return Futures.immediateFuture( createSessionDownRpcResult() );
}
- final Request req = new Request(new UncancellableFuture<RpcResult<NetconfMessage>>(true), message, rpc);
+ final Request req = new Request( new UncancellableFuture<RpcResult<NetconfMessage>>(true),
+ message );
requests.add(req);
session.sendMessage(req.request).addListener(new FutureListener<Void>() {
@Override
public void operationComplete(final Future<Void> future) throws Exception {
- if (!future.isSuccess()) {
+ if( !future.isSuccess() ) {
// We expect that a session down will occur at this point
- logger.debug("{}: Failed to send request {}", id, XmlUtil.toString(req.request.getDocument()), future.cause());
- req.future.setException(future.cause());
- } else {
- logger.trace("{}: Finished sending request {}", id, req.request);
+ logger.debug( "{}: Failed to send request {}", id,
+ XmlUtil.toString(req.request.getDocument()), future.cause() );
+
+ if( future.cause() != null ) {
+ req.future.set( createErrorRpcResult( RpcError.ErrorType.TRANSPORT,
+ future.cause().getLocalizedMessage() ) );
+ } else {
+ req.future.set( createSessionDownRpcResult() ); // assume session is down
+ }
+ req.future.setException( future.cause() );
+ }
+ else {
+ logger.trace( "Finished sending request {}", req.request );
}
}
});
private static final class Request {
final UncancellableFuture<RpcResult<NetconfMessage>> future;
final NetconfMessage request;
- final QName rpc;
- private Request(final UncancellableFuture<RpcResult<NetconfMessage>> future, final NetconfMessage request, final QName rpc) {
+ private Request(final UncancellableFuture<RpcResult<NetconfMessage>> future,
+ final NetconfMessage request) {
this.future = future;
this.request = request;
- this.rpc = rpc;
}
}
}
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import javax.annotation.Nullable;
import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
import org.opendaylight.controller.netconf.api.NetconfMessage;
import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
-import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.controller.sal.common.util.RpcErrors;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.Node;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
return new CompositeNodeTOImpl(argument.getNodeType(), null, list);
}
- public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) {
+ public static void checkValidReply(final NetconfMessage input, final NetconfMessage output)
+ throws NetconfDocumentedException {
final String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id");
final String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id");
if(inputMsgId.equals(outputMsgId) == false) {
- final String requestXml = XmlUtil.toString(input.getDocument());
- final String responseXml = XmlUtil.toString(output.getDocument());
- throw new IllegalStateException(String.format("Rpc request and reply message IDs must be same. Request: %s, response: %s", requestXml, responseXml));
+ Map<String,String> errorInfo = ImmutableMap.<String,String>builder()
+ .put( "actual-message-id", outputMsgId )
+ .put( "expected-message-id", inputMsgId )
+ .build();
+
+ throw new NetconfDocumentedException( "Response message contained unknown \"message-id\"",
+ null, NetconfDocumentedException.ErrorType.protocol,
+ NetconfDocumentedException.ErrorTag.bad_attribute,
+ NetconfDocumentedException.ErrorSeverity.error, errorInfo );
}
}
public static void checkSuccessReply(final NetconfMessage output) throws NetconfDocumentedException {
if(NetconfMessageUtil.isErrorMessage(output)) {
- throw new IllegalStateException(String.format("Response contains error: %s", XmlUtil.toString(output.getDocument())));
+ throw NetconfDocumentedException.fromXMLDocument( output.getDocument() );
+ }
+ }
+
+ public static RpcError toRpcError( NetconfDocumentedException ex )
+ {
+ StringBuilder infoBuilder = new StringBuilder();
+ Map<String, String> errorInfo = ex.getErrorInfo();
+ if( errorInfo != null )
+ {
+ for( Entry<String,String> e: errorInfo.entrySet() ) {
+ infoBuilder.append( '<' ).append( e.getKey() ).append( '>' ).append( e.getValue() )
+ .append( "</" ).append( e.getKey() ).append( '>' );
+
+ }
+ }
+
+ return RpcErrors.getRpcError( null, ex.getErrorTag().getTagValue(), infoBuilder.toString(),
+ toRpcErrorSeverity( ex.getErrorSeverity() ), ex.getLocalizedMessage(),
+ toRpcErrorType( ex.getErrorType() ), ex.getCause() );
+ }
+
+ private static ErrorSeverity toRpcErrorSeverity( NetconfDocumentedException.ErrorSeverity severity ) {
+ switch( severity ) {
+ case warning:
+ return RpcError.ErrorSeverity.WARNING;
+ default:
+ return RpcError.ErrorSeverity.ERROR;
+ }
+ }
+
+ private static RpcError.ErrorType toRpcErrorType( NetconfDocumentedException.ErrorType type )
+ {
+ switch( type ) {
+ case protocol:
+ return RpcError.ErrorType.PROTOCOL;
+ case rpc:
+ return RpcError.ErrorType.RPC;
+ case transport:
+ return RpcError.ErrorType.TRANSPORT;
+ default:
+ return RpcError.ErrorType.APPLICATION;
}
}
import org.junit.Test;
import org.mockito.Mockito;
import org.opendaylight.controller.netconf.api.NetconfMessage;
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
import org.opendaylight.controller.sal.common.util.Rpcs;
import org.opendaylight.controller.sal.connect.api.MessageTransformer;
import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications 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.sal.connect.netconf.listener;
+
+import io.netty.channel.ChannelFuture;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+
+import java.io.ByteArrayInputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
+import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
+
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.client.NetconfClientSession;
+import org.opendaylight.controller.sal.connect.api.RemoteDevice;
+import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator;
+import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.ListenableFuture;
+
+public class NetconfDeviceCommunicatorTest {
+
+ @Mock
+ NetconfClientSession mockSession;
+
+ @Mock
+ RemoteDevice<NetconfSessionCapabilities, NetconfMessage> mockDevice;
+
+ NetconfDeviceCommunicator communicator;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks( this );
+
+ communicator = new NetconfDeviceCommunicator( new RemoteDeviceId( "test" ), mockDevice );
+ }
+
+ @SuppressWarnings("unchecked")
+ void setupSession()
+ {
+ doReturn( Collections.<String>emptySet() ).when( mockSession ).getServerCapabilities();
+ doNothing().when( mockDevice ).onRemoteSessionUp( any( NetconfSessionCapabilities.class ),
+ any( RemoteDeviceCommunicator.class ) );
+ communicator.onSessionUp( mockSession );
+ }
+
+ private ListenableFuture<RpcResult<NetconfMessage>> sendRequest() throws Exception {
+ return sendRequest( UUID.randomUUID().toString() );
+ }
+
+ @SuppressWarnings("unchecked")
+ private ListenableFuture<RpcResult<NetconfMessage>> sendRequest( String messageID ) throws Exception {
+ Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ Element element = doc.createElement( "request" );
+ element.setAttribute( "message-id", messageID );
+ doc.appendChild( element );
+ NetconfMessage message = new NetconfMessage( doc );
+
+ ChannelFuture mockChannelFuture = mock( ChannelFuture.class );
+ doReturn( mockChannelFuture ).when( mockChannelFuture )
+ .addListener( any( (GenericFutureListener.class ) ) );
+ doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) );
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture =
+ communicator.sendRequest( message, QName.create( "mock rpc" ) );
+
+ assertNotNull( "ListenableFuture is null", resultFuture );
+ return resultFuture;
+ }
+
+ @Test
+ public void testOnSessionUp() {
+ String testCapability = "urn:opendaylight:params:xml:ns:test?module=test-module&revision=2014-06-02";
+ Collection<String> serverCapabilities =
+ Sets.newHashSet( NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString(),
+ NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString(),
+ testCapability );
+ doReturn( serverCapabilities ).when( mockSession ).getServerCapabilities();
+
+ ArgumentCaptor<NetconfSessionCapabilities> netconfSessionCapabilities =
+ ArgumentCaptor.forClass( NetconfSessionCapabilities.class );
+ doNothing().when( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
+
+ communicator.onSessionUp( mockSession );
+
+ verify( mockSession ).getServerCapabilities();
+ verify( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) );
+
+ NetconfSessionCapabilities actualCapabilites = netconfSessionCapabilities.getValue();
+ assertEquals( "containsCapability", true, actualCapabilites.containsCapability(
+ NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString() ) );
+ assertEquals( "containsCapability", true, actualCapabilites.containsCapability( testCapability ) );
+ assertEquals( "getModuleBasedCaps", Sets.newHashSet(
+ QName.create( "urn:opendaylight:params:xml:ns:test", "2014-06-02", "test-module" )),
+ actualCapabilites.getModuleBasedCaps() );
+ assertEquals( "isRollbackSupported", true, actualCapabilites.isRollbackSupported() );
+ assertEquals( "isMonitoringSupported", true, actualCapabilites.isMonitoringSupported() );
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test(timeout=5000)
+ public void testOnSessionDown() throws Exception {
+ setupSession();
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture1 = sendRequest();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest();
+
+ doNothing().when( mockDevice ).onRemoteSessionDown();
+
+ communicator.onSessionDown( mockSession, new Exception( "mock ex" ) );
+
+ verifyErrorRpcResult( resultFuture1.get(), RpcError.ErrorType.TRANSPORT, "operation-failed" );
+ verifyErrorRpcResult( resultFuture2.get(), RpcError.ErrorType.TRANSPORT, "operation-failed" );
+
+ verify( mockDevice ).onRemoteSessionDown();
+
+ reset( mockDevice );
+
+ communicator.onSessionDown( mockSession, new Exception( "mock ex" ) );
+
+ verify( mockDevice, never() ).onRemoteSessionDown();
+ }
+
+ @Test
+ public void testOnSessionTerminated() throws Exception {
+ setupSession();
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest();
+
+ doNothing().when( mockDevice ).onRemoteSessionDown();
+
+ String reasonText = "testing terminate";
+ NetconfTerminationReason reason = new NetconfTerminationReason( reasonText );
+ communicator.onSessionTerminated( mockSession, reason );
+
+ RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.TRANSPORT,
+ "operation-failed" );
+ assertEquals( "RpcError message", reasonText, rpcError.getMessage() );
+
+ verify( mockDevice ).onRemoteSessionDown();
+ }
+
+ @Test
+ public void testClose() throws Exception {
+ communicator.close();
+ verify( mockDevice, never() ).onRemoteSessionDown();
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test
+ public void testSendRequest() throws Exception {
+ setupSession();
+
+ NetconfMessage message = new NetconfMessage(
+ DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );
+ QName rpc = QName.create( "mock rpc" );
+
+ ArgumentCaptor<GenericFutureListener> futureListener =
+ ArgumentCaptor.forClass( GenericFutureListener.class );
+
+ ChannelFuture mockChannelFuture = mock( ChannelFuture.class );
+ doReturn( mockChannelFuture ).when( mockChannelFuture ).addListener( futureListener.capture() );
+ doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) );
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest( message, rpc );
+
+ verify( mockSession ).sendMessage( same( message ) );
+
+ assertNotNull( "ListenableFuture is null", resultFuture );
+
+ verify( mockChannelFuture ).addListener( futureListener.capture() );
+ Future<Void> operationFuture = mock( Future.class );
+ doReturn( true ).when( operationFuture ).isSuccess();
+ doReturn( true ).when( operationFuture ).isDone();
+ futureListener.getValue().operationComplete( operationFuture );
+
+ try {
+ resultFuture.get( 1, TimeUnit.MILLISECONDS ); // verify it's not cancelled or has an error set
+ }
+ catch( TimeoutException e ) {} // expected
+ }
+
+ @Test
+ public void testSendRequestWithNoSession() throws Exception {
+ NetconfMessage message = new NetconfMessage(
+ DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );
+ QName rpc = QName.create( "mock rpc" );
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest( message, rpc );
+
+ assertNotNull( "ListenableFuture is null", resultFuture );
+
+ // Should have an immediate result
+ RpcResult<NetconfMessage> rpcResult = resultFuture.get( 3, TimeUnit.MILLISECONDS );
+
+ verifyErrorRpcResult( rpcResult, RpcError.ErrorType.TRANSPORT, "operation-failed" );
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ @Test
+ public void testSendRequestWithWithSendFailure() throws Exception {
+ setupSession();
+
+ NetconfMessage message = new NetconfMessage(
+ DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() );
+ QName rpc = QName.create( "mock rpc" );
+
+ ArgumentCaptor<GenericFutureListener> futureListener =
+ ArgumentCaptor.forClass( GenericFutureListener.class );
+
+ ChannelFuture mockChannelFuture = mock( ChannelFuture.class );
+ doReturn( mockChannelFuture ).when( mockChannelFuture ).addListener( futureListener.capture() );
+ doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) );
+
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = communicator.sendRequest( message, rpc );
+
+ assertNotNull( "ListenableFuture is null", resultFuture );
+
+ verify( mockChannelFuture ).addListener( futureListener.capture() );
+
+ Future<Void> operationFuture = mock( Future.class );
+ doReturn( false ).when( operationFuture ).isSuccess();
+ doReturn( true ).when( operationFuture ).isDone();
+ doReturn( new Exception( "mock error" ) ).when( operationFuture ).cause();
+ futureListener.getValue().operationComplete( operationFuture );
+
+ // Should have an immediate result
+ RpcResult<NetconfMessage> rpcResult = resultFuture.get( 3, TimeUnit.MILLISECONDS );
+
+ RpcError rpcError = verifyErrorRpcResult( rpcResult, RpcError.ErrorType.TRANSPORT, "operation-failed" );
+ assertEquals( "RpcError message contains \"mock error\"", true,
+ rpcError.getMessage().contains( "mock error" ) );
+ }
+
+ private NetconfMessage createSuccessResponseMessage( String messageID ) throws ParserConfigurationException {
+ Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ Element rpcReply = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY );
+ rpcReply.setAttribute( "message-id", messageID );
+ Element element = doc.createElementNS( "ns", "data" );
+ element.setTextContent( messageID );
+ rpcReply.appendChild( element );
+ doc.appendChild( rpcReply );
+
+ return new NetconfMessage( doc );
+ }
+
+ @Test
+ public void testOnSuccessfulResponseMessage() throws Exception {
+ setupSession();
+
+ String messageID1 = UUID.randomUUID().toString();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture1 = sendRequest( messageID1 );
+
+ String messageID2 = UUID.randomUUID().toString();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture2 = sendRequest( messageID2 );
+
+ communicator.onMessage( mockSession, createSuccessResponseMessage( messageID1 ) );
+ communicator.onMessage( mockSession, createSuccessResponseMessage( messageID2 ) );
+
+ verifyResponseMessage( resultFuture1.get(), messageID1 );
+ verifyResponseMessage( resultFuture2.get(), messageID2 );
+ }
+
+ @Test
+ public void testOnResponseMessageWithError() throws Exception {
+ setupSession();
+
+ String messageID = UUID.randomUUID().toString();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest( messageID );
+
+ communicator.onMessage( mockSession, createErrorResponseMessage( messageID ) );
+
+ RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.RPC,
+ "missing-attribute" );
+ assertEquals( "RpcError message", "Missing attribute", rpcError.getMessage() );
+
+ String errorInfo = rpcError.getInfo();
+ assertNotNull( "RpcError info is null", errorInfo );
+ assertEquals( "Error info contains \"foo\"", true,
+ errorInfo.contains( "<bad-attribute>foo</bad-attribute>" ) );
+ assertEquals( "Error info contains \"bar\"", true,
+ errorInfo.contains( "<bad-element>bar</bad-element>" ) );
+ }
+
+ @Test
+ public void testOnResponseMessageWithWrongMessageID() throws Exception {
+ setupSession();
+
+ String messageID = UUID.randomUUID().toString();
+ ListenableFuture<RpcResult<NetconfMessage>> resultFuture = sendRequest( messageID );
+
+ communicator.onMessage( mockSession, createSuccessResponseMessage( UUID.randomUUID().toString() ) );
+
+ RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.PROTOCOL,
+ "bad-attribute" );
+ assertEquals( "RpcError message non-empty", true,
+ !Strings.isNullOrEmpty( rpcError.getMessage() ) );
+
+ String errorInfo = rpcError.getInfo();
+ assertNotNull( "RpcError info is null", errorInfo );
+ assertEquals( "Error info contains \"actual-message-id\"", true,
+ errorInfo.contains( "actual-message-id" ) );
+ assertEquals( "Error info contains \"expected-message-id\"", true,
+ errorInfo.contains( "expected-message-id" ) );
+ }
+
+ private NetconfMessage createErrorResponseMessage( String messageID ) throws Exception {
+ String xmlStr =
+ "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"" +
+ " message-id=\"" + messageID + "\">" +
+ " <rpc-error>" +
+ " <error-type>rpc</error-type>" +
+ " <error-tag>missing-attribute</error-tag>" +
+ " <error-severity>error</error-severity>" +
+ " <error-message>Missing attribute</error-message>" +
+ " <error-info>" +
+ " <bad-attribute>foo</bad-attribute>" +
+ " <bad-element>bar</bad-element>" +
+ " </error-info>" +
+ " </rpc-error>" +
+ "</rpc-reply>";
+
+ ByteArrayInputStream bis = new ByteArrayInputStream( xmlStr.getBytes() );
+ Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse( bis );
+ return new NetconfMessage( doc );
+ }
+
+ private void verifyResponseMessage( RpcResult<NetconfMessage> rpcResult, String dataText ) {
+ assertNotNull( "RpcResult is null", rpcResult );
+ assertEquals( "isSuccessful", true, rpcResult.isSuccessful() );
+ NetconfMessage messageResult = rpcResult.getResult();
+ assertNotNull( "getResult", messageResult );
+// List<SimpleNode<?>> nodes = messageResult.getSimpleNodesByName(
+// QName.create( URI.create( "ns" ), null, "data" ) );
+// assertNotNull( "getSimpleNodesByName", nodes );
+// assertEquals( "List<SimpleNode<?>> size", 1, nodes.size() );
+// assertEquals( "SimpleNode value", dataText, nodes.iterator().next().getValue() );
+ }
+
+ private RpcError verifyErrorRpcResult( RpcResult<NetconfMessage> rpcResult,
+ RpcError.ErrorType expErrorType, String expErrorTag ) {
+ assertNotNull( "RpcResult is null", rpcResult );
+ assertEquals( "isSuccessful", false, rpcResult.isSuccessful() );
+ assertNotNull( "RpcResult errors is null", rpcResult.getErrors() );
+ assertEquals( "Errors size", 1, rpcResult.getErrors().size() );
+ RpcError rpcError = rpcResult.getErrors().iterator().next();
+ assertEquals( "getErrorSeverity", RpcError.ErrorSeverity.ERROR, rpcError.getSeverity() );
+ assertEquals( "getErrorType", expErrorType, rpcError.getErrorType() );
+ assertEquals( "getErrorTag", expErrorTag, rpcError.getTag() );
+ assertTrue( "getMessage is empty", StringUtils.isNotEmpty( rpcError.getMessage() ) );
+ return rpcError;
+ }
+}
@Path("/config/{identifier:.+}")
@Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
- public StructuredData readConfigurationData(@Encoded @PathParam("identifier") String identifier);
+ public StructuredData readConfigurationData(@Encoded @PathParam("identifier") String identifier,
+ @Context UriInfo depth);
@GET
@Path("/operational/{identifier:.+}")
@Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML,
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
- public StructuredData readOperationalData(@Encoded @PathParam("identifier") String identifier);
+ public StructuredData readOperationalData(@Encoded @PathParam("identifier") String identifier,
+ @Context UriInfo depth);
@PUT
@Path("/config/{identifier:.+}")
*/
package org.opendaylight.controller.sal.restconf.impl;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
-
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
-
import org.apache.commons.lang3.StringUtils;
import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
import org.opendaylight.yangtools.yang.data.api.Node;
import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
public class RestconfImpl implements RestconfService {
private final static RestconfImpl INSTANCE = new RestconfImpl();
operationsAsData.add(immutableSimpleNode);
String name = module.getName();
- LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, null);
+ LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
+ SchemaPath.create(true, QName.create("dummy")));
final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
fakeRpcSchemaNode.setAugmenting(true);
}
@Override
- public StructuredData readConfigurationData(final String identifier) {
+ public StructuredData readConfigurationData(final String identifier, UriInfo info) {
final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
CompositeNode data = null;
MountInstance mountPoint = iiWithData.getMountPoint();
data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
}
+ data = pruneDataAtDepth( data, parseDepthParameter( info ) );
return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
}
+ @SuppressWarnings("unchecked")
+ private <T extends Node<?>> T pruneDataAtDepth( T node, Integer depth ) {
+ if( depth == null ) {
+ return node;
+ }
+
+ if( node instanceof CompositeNode ) {
+ ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
+ if( depth > 1 ) {
+ for( Node<?> childNode: ((CompositeNode)node).getValue() ) {
+ newChildNodes.add( pruneDataAtDepth( childNode, depth - 1 ) );
+ }
+ }
+
+ return (T) ImmutableCompositeNode.create( node.getNodeType(), newChildNodes.build() );
+ }
+ else { // SimpleNode
+ return node;
+ }
+ }
+
+ private Integer parseDepthParameter( UriInfo info ) {
+ String param = info.getQueryParameters( false ).getFirst( "depth" );
+ if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) {
+ return null;
+ }
+
+ try {
+ Integer depth = Integer.valueOf( param );
+ if( depth < 1 ) {
+ throw new RestconfDocumentedException( new RestconfError(
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth,
+ null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
+ }
+
+ return depth;
+ }
+ catch( NumberFormatException e ) {
+ throw new RestconfDocumentedException( new RestconfError(
+ ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+ "Invalid depth parameter: " + e.getMessage(),
+ null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
+ }
+ }
+
@Override
- public StructuredData readOperationalData(final String identifier) {
+ public StructuredData readOperationalData(final String identifier, UriInfo info) {
final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
CompositeNode data = null;
MountInstance mountPoint = iiWithData.getMountPoint();
data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
}
+ data = pruneDataAtDepth( data, parseDepthParameter( info ) );
return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint);
}
import java.io.IOException;
import java.util.Collections;
-
import javax.ws.rs.WebApplicationException;
-
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
import org.opendaylight.controller.sal.restconf.impl.test.DummyType;
import org.opendaylight.controller.sal.restconf.impl.test.TestUtils;
import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader;
+import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.ModifyAction;
import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
import org.slf4j.Logger;
private DataSchemaNode prepareDataSchemaNode() {
ContainerSchemaNodeBuilder contBuild = new ContainerSchemaNodeBuilder("module", 1, TestUtils.buildQName("cont",
- "simple:uri", "2012-12-17"), null);
+ "simple:uri", "2012-12-17"), SchemaPath.create(true, QName.create("dummy")));
LeafSchemaNodeBuilder leafBuild = new LeafSchemaNodeBuilder("module", 2, TestUtils.buildQName("lf1",
- "simple:uri", "2012-12-17"), null);
+ "simple:uri", "2012-12-17"), SchemaPath.create(true, QName.create("dummy")));
leafBuild.setType(new DummyType());
leafBuild.setConfiguration(true);
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
String uriPrefix = "/config/";
String uriPath = "ietf-interfaces:interfaces";
String uri = uriPrefix + uriPath;
- when(restconfService.readConfigurationData(uriPath)).thenReturn(null);
+ when(restconfService.readConfigurationData(eq(uriPath), any(UriInfo.class))).thenReturn(null);
get(uri, Draft02.MediaTypes.DATA+JSON);
- verify(restconfService, times(1)).readConfigurationData(uriPath);
+ verify(restconfService, times(1)).readConfigurationData(eq(uriPath), any(UriInfo.class));
get(uri, Draft02.MediaTypes.DATA+XML);
- verify(restconfService, times(2)).readConfigurationData(uriPath);
+ verify(restconfService, times(2)).readConfigurationData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.APPLICATION_JSON);
- verify(restconfService, times(3)).readConfigurationData(uriPath);
+ verify(restconfService, times(3)).readConfigurationData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.APPLICATION_XML);
- verify(restconfService, times(4)).readConfigurationData(uriPath);
+ verify(restconfService, times(4)).readConfigurationData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.TEXT_XML);
- verify(restconfService, times(5)).readConfigurationData(uriPath);
+ verify(restconfService, times(5)).readConfigurationData(eq(uriPath), any(UriInfo.class));
// negative tests
get(uri, MediaType.TEXT_PLAIN);
- verify(restconfService, times(5)).readConfigurationData(uriPath);
+ verify(restconfService, times(5)).readConfigurationData(eq(uriPath), any(UriInfo.class));
}
@Test
String uriPrefix = "/operational/";
String uriPath = "ietf-interfaces:interfaces";
String uri = uriPrefix + uriPath;
- when(restconfService.readOperationalData(uriPath)).thenReturn(null);
+ when(restconfService.readOperationalData(eq(uriPath), any(UriInfo.class))).thenReturn(null);
get(uri, Draft02.MediaTypes.DATA+JSON);
- verify(restconfService, times(1)).readOperationalData(uriPath);
+ verify(restconfService, times(1)).readOperationalData(eq(uriPath), any(UriInfo.class));
get(uri, Draft02.MediaTypes.DATA+XML);
- verify(restconfService, times(2)).readOperationalData(uriPath);
+ verify(restconfService, times(2)).readOperationalData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.APPLICATION_JSON);
- verify(restconfService, times(3)).readOperationalData(uriPath);
+ verify(restconfService, times(3)).readOperationalData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.APPLICATION_XML);
- verify(restconfService, times(4)).readOperationalData(uriPath);
+ verify(restconfService, times(4)).readOperationalData(eq(uriPath), any(UriInfo.class));
get(uri, MediaType.TEXT_XML);
- verify(restconfService, times(5)).readOperationalData(uriPath);
+ verify(restconfService, times(5)).readOperationalData(eq(uriPath), any(UriInfo.class));
// negative tests
get(uri, MediaType.TEXT_PLAIN);
- verify(restconfService, times(5)).readOperationalData(uriPath);
+ verify(restconfService, times(5)).readOperationalData(eq(uriPath), any(UriInfo.class));
}
@Test
*/
package org.opendaylight.controller.sal.restconf.impl.test;
-import static junit.framework.Assert.assertNotNull;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.FileNotFoundException;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.core.api.mount.MountService;
import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider;
import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper;
import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
+import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException;
import org.opendaylight.controller.sal.restconf.impl.RestconfImpl;
import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
public class RestGetOperationTest extends JerseyTest {
+ static class NodeData {
+ Object key;
+ Object data; // List for a CompositeNode, value Object for a SimpleNode
+
+ NodeData( Object key, Object data ) {
+ this.key = key;
+ this.data = data;
+ }
+ }
+
private static BrokerFacade brokerFacade;
private static RestconfImpl restconfImpl;
private static SchemaContext schemaContextYangsIetf;
return null;
}
+ @Test
+ public void getDataWithUriDepthParameterTest() throws UnsupportedEncodingException {
+
+ ControllerContext.getInstance().setGlobalSchema( schemaContextModules );
+
+ CompositeNode depth1Cont = toCompositeNode(
+ toCompositeNodeData( toNestedQName( "depth1-cont" ),
+ toCompositeNodeData( toNestedQName( "depth2-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth3-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth4-cont1" ),
+ toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
+ ),
+ toCompositeNodeData( toNestedQName( "depth2-cont2" ),
+ toCompositeNodeData( toNestedQName( "depth3-cont2" ),
+ toCompositeNodeData( toNestedQName( "depth4-cont2" ),
+ toSimpleNodeData( toNestedQName( "depth5-leaf2" ), "depth5-leaf2-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth4-leaf2" ), "depth4-leaf2-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth3-leaf2" ), "depth3-leaf2-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth2-leaf1" ), "depth2-leaf1-value" )
+ ) );
+
+ when( brokerFacade.readConfigurationData( any( InstanceIdentifier.class ) ) )
+ .thenReturn( depth1Cont );
+
+ // Test config with depth 1
+
+ Response response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "1" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response, expectEmptyContainer( "depth1-cont" ) );
+
+ // Test config with depth 2
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "2" )
+ .request( "application/xml" ).get();
+
+// String xml="<depth1-cont><depth2-cont1/><depth2-cont2/><depth2-leaf1>depth2-leaf1-value</depth2-leaf1></depth1-cont>";
+// Response mr=mock(Response.class);
+// when(mr.getEntity()).thenReturn( new java.io.StringBufferInputStream(xml) );
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectEmptyContainer( "depth2-cont1" ),
+ expectEmptyContainer( "depth2-cont2" ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test config with depth 3
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "3" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectEmptyContainer( "depth3-cont1" ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectEmptyContainer( "depth3-cont2" ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test config with depth 4
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "4" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectEmptyContainer( "depth4-cont1" ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectContainer( "depth3-cont2",
+ expectEmptyContainer( "depth4-cont2" ),
+ expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
+ ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test config with depth 5
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "5" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectContainer( "depth4-cont1",
+ expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
+ ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectContainer( "depth3-cont2",
+ expectContainer( "depth4-cont2",
+ expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
+ ),
+ expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
+ ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test config with depth unbounded
+
+ response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "unbounded" )
+ .request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth1-cont",
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectContainer( "depth4-cont1",
+ expectLeaf( "depth5-leaf1", "depth5-leaf1-value" )
+ ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ),
+ expectContainer( "depth2-cont2",
+ expectContainer( "depth3-cont2",
+ expectContainer( "depth4-cont2",
+ expectLeaf( "depth5-leaf2", "depth5-leaf2-value" )
+ ),
+ expectLeaf( "depth4-leaf2", "depth4-leaf2-value" )
+ ),
+ expectLeaf( "depth3-leaf2", "depth3-leaf2-value" )
+ ),
+ expectLeaf( "depth2-leaf1", "depth2-leaf1-value" )
+ ) );
+
+ // Test operational
+
+ CompositeNode depth2Cont1 = toCompositeNode(
+ toCompositeNodeData( toNestedQName( "depth2-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth3-cont1" ),
+ toCompositeNodeData( toNestedQName( "depth4-cont1" ),
+ toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" )
+ ),
+ toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" )
+ ) );
+
+ when( brokerFacade.readOperationalData( any( InstanceIdentifier.class ) ) )
+ .thenReturn( depth2Cont1 );
+
+ response = target( "/operational/nested-module:depth1-cont/depth2-cont1" )
+ .queryParam( "depth", "3" ).request( "application/xml" ).get();
+
+ verifyXMLResponse( response,
+ expectContainer( "depth2-cont1",
+ expectContainer( "depth3-cont1",
+ expectEmptyContainer( "depth4-cont1" ),
+ expectLeaf( "depth4-leaf1", "depth4-leaf1-value" )
+ ),
+ expectLeaf( "depth3-leaf1", "depth3-leaf1-value" )
+ ) );
+ }
+
+ @Test
+ public void getDataWithInvalidDepthParameterTest() {
+
+ ControllerContext.getInstance().setGlobalSchema( schemaContextModules );
+
+ final MultivaluedMap<String,String> paramMap = new MultivaluedHashMap<>();
+ paramMap.putSingle( "depth", "1o" );
+ UriInfo mockInfo = mock( UriInfo.class );
+ when( mockInfo.getQueryParameters( false ) ).thenAnswer(
+ new Answer<MultivaluedMap<String,String>>() {
+ @Override
+ public MultivaluedMap<String, String> answer( InvocationOnMock invocation ) {
+ return paramMap;
+ }
+ } );
+
+ getDataWithInvalidDepthParameterTest( mockInfo );
+
+ paramMap.putSingle( "depth", "0" );
+ getDataWithInvalidDepthParameterTest( mockInfo );
+
+ paramMap.putSingle( "depth", "-1" );
+ getDataWithInvalidDepthParameterTest( mockInfo );
+ }
+
+ private void getDataWithInvalidDepthParameterTest( UriInfo uriInfo ) {
+ try {
+ restconfImpl.readConfigurationData( "nested-module:depth1-cont", uriInfo );
+ fail( "Expected RestconfDocumentedException" );
+ }
+ catch( RestconfDocumentedException e ) {
+ assertTrue( "Unexpected error message: " + e.getErrors().get( 0 ).getErrorMessage(),
+ e.getErrors().get( 0 ).getErrorMessage().contains( "depth" ) );
+ }
+ }
+
+ private void verifyXMLResponse( Response response, NodeData nodeData ) {
+
+ Document doc = TestUtils.loadDocumentFrom( (InputStream) response.getEntity() );
+ assertNotNull( "Could not parse XML document", doc );
+
+ //System.out.println(TestUtils.getDocumentInPrintableForm( doc ));
+
+ verifyContainerElement( doc.getDocumentElement(), nodeData );
+ }
+
+ @SuppressWarnings("unchecked")
+ private void verifyContainerElement( Element element, NodeData nodeData ) {
+
+ assertEquals( "Element local name", nodeData.key, element.getNodeName() );
+
+ NodeList childNodes = element.getChildNodes();
+ if( nodeData.data == null ) { // empty container
+ assertTrue( "Expected no child elements for \"" + element.getNodeName() + "\"",
+ childNodes.getLength() == 0 );
+ return;
+ }
+
+ Map<String,NodeData> expChildMap = Maps.newHashMap();
+ for( NodeData expChild: (List<NodeData>)nodeData.data ) {
+ expChildMap.put( expChild.key.toString(), expChild );
+ }
+
+ for( int i = 0; i < childNodes.getLength(); i++ ) {
+ org.w3c.dom.Node actualChild = childNodes.item( i );
+ if( !( actualChild instanceof Element ) ) {
+ continue;
+ }
+
+ Element actualElement = (Element)actualChild;
+ NodeData expChild = expChildMap.remove( actualElement.getNodeName() );
+ assertNotNull( "Unexpected child element for parent \"" + element.getNodeName() +
+ "\": " + actualElement.getNodeName(), expChild );
+
+ if( expChild.data == null || expChild.data instanceof List ) {
+ verifyContainerElement( actualElement, expChild );
+ }
+ else {
+ assertEquals( "Text content for element: " + actualElement.getNodeName(),
+ expChild.data, actualElement.getTextContent() );
+ }
+ }
+
+ if( !expChildMap.isEmpty() ) {
+ fail( "Missing elements for parent \"" + element.getNodeName() +
+ "\": " + expChildMap.keySet() );
+ }
+ }
+
+ private NodeData expectContainer( String name, NodeData... childData ) {
+ return new NodeData( name, Lists.newArrayList( childData ) );
+ }
+
+ private NodeData expectEmptyContainer( String name ) {
+ return new NodeData( name, null );
+ }
+
+ private NodeData expectLeaf( String name, Object value ) {
+ return new NodeData( name, value );
+ }
+
+ private QName toNestedQName( String localName ) {
+ return QName.create( "urn:nested:module", "2014-06-3", localName );
+ }
+
+ @SuppressWarnings("unchecked")
+ private CompositeNode toCompositeNode( NodeData nodeData ) {
+ CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
+ builder.setQName( (QName) nodeData.key );
+
+ for( NodeData child: (List<NodeData>)nodeData.data ) {
+ if( child.data instanceof List ) {
+ builder.add( toCompositeNode( child ) );
+ }
+ else {
+ builder.addLeaf( (QName) child.key, child.data );
+ }
+ }
+
+ return builder.toInstance();
+ }
+
+ private NodeData toCompositeNodeData( QName key, NodeData... childData ) {
+ return new NodeData( key, Lists.newArrayList( childData ) );
+ }
+
+ private NodeData toSimpleNodeData( QName key, Object value ) {
+ return new NodeData( key, value );
+ }
}
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
void stageMockEx( final RestconfDocumentedException ex ) {
reset( mockRestConf );
- when( mockRestConf.readOperationalData( any( String.class ) ) ).thenThrow( ex );
+ when( mockRestConf.readOperationalData( any( String.class ), any( UriInfo.class ) ) ).thenThrow( ex );
}
void testJsonResponse( final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType,
// The StructuredDataToJsonProvider should throw a RestconfDocumentedException with no data
- when( mockRestConf.readOperationalData( any( String.class ) ) )
- .thenReturn( new StructuredData( null, null, null ) );
+ when( mockRestConf.readOperationalData( any( String.class ), any( UriInfo.class ) ) )
+ .thenReturn( new StructuredData( null, null, null ) );
Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get();
--- /dev/null
+module nested-module {
+ namespace "urn:nested:module";
+ prefix "nested";
+ revision "2014-06-3";
+
+ container depth1-cont {
+ container depth2-cont1 {
+ container depth3-cont1 {
+ container depth4-cont1 {
+ leaf depth5-leaf1 {
+ type string;
+ }
+ }
+
+ leaf depth4-leaf1 {
+ type string;
+ }
+ }
+
+ leaf depth3-leaf1 {
+ type string;
+ }
+ }
+
+ container depth2-cont2 {
+ container depth3-cont2 {
+ container depth4-cont2 {
+ leaf depth5-leaf2 {
+ type string;
+ }
+ }
+
+ leaf depth4-leaf2 {
+ type string;
+ }
+ }
+
+ leaf depth3-leaf2 {
+ type string;
+ }
+ }
+
+ leaf depth2-leaf1 {
+ type string;
+ }
+ }
+}
\ No newline at end of file
<artifactId>guava</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.3.2</version>
+ <!--$NO-MVN-MAN-VER$ -->
+ </dependency>
+
<!-- Jax rs -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
import java.util.Collection;
import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
import org.opendaylight.controller.sal.rest.doc.impl.ApiDocGenerator;
+import org.opendaylight.controller.sal.rest.doc.mountpoints.MountPointSwagger;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class DocProvider implements BundleActivator, ServiceTrackerCustomizer<Broker, Broker>, Provider, AutoCloseable {
+public class DocProvider implements BundleActivator, ServiceTrackerCustomizer<Broker, Broker>,
+ Provider, AutoCloseable {
- private static final Logger _logger = LoggerFactory.getLogger(DocProvider.class);
+ private final Logger _logger = LoggerFactory.getLogger(DocProvider.class);
private ServiceTracker<Broker, Broker> brokerServiceTracker;
private BundleContext bundleContext;
private Broker.ProviderSession session;
+ private final List<AutoCloseable> toClose = new LinkedList<>();
+
@Override
public void close() throws Exception {
stop(bundleContext);
}
@Override
- public void onSessionInitiated(final Broker.ProviderSession providerSession) {
+ public void onSessionInitiated(Broker.ProviderSession providerSession) {
SchemaService schemaService = providerSession.getService(SchemaService.class);
ApiDocGenerator.getInstance().setSchemaService(schemaService);
+ MountProvisionService mountService = providerSession
+ .getService(MountProvisionService.class);
+ ListenerRegistration<MountProvisionListener> registration = mountService
+ .registerProvisionListener(MountPointSwagger.getInstance());
+ MountPointSwagger.getInstance().setGlobalSchema(schemaService);
+ synchronized (toClose) {
+ toClose.add(registration);
+ }
+ MountPointSwagger.getInstance().setMountService(mountService);
+
_logger.debug("Restconf API Explorer started");
}
}
@Override
- public void start(final BundleContext context) throws Exception {
+ public void start(BundleContext context) throws Exception {
bundleContext = context;
- brokerServiceTracker = new ServiceTracker<>(context, Broker.class, this);
+ brokerServiceTracker = new ServiceTracker(context, Broker.class, this);
brokerServiceTracker.open();
}
@Override
- public void stop(final BundleContext context) throws Exception {
- if (brokerServiceTracker != null) {
+ public void stop(BundleContext context) throws Exception {
+ if (brokerServiceTracker != null)
brokerServiceTracker.close();
- }
- if (session != null) {
+ if (session != null)
session.close();
+
+ synchronized (toClose) {
+ for (AutoCloseable close : toClose) {
+ close.close();
+ }
}
}
@Override
- public Broker addingService(final ServiceReference<Broker> reference) {
+ public Broker addingService(ServiceReference<Broker> reference) {
Broker broker = bundleContext.getService(reference);
session = broker.registerProvider(this, bundleContext);
return broker;
}
@Override
- public void modifiedService(final ServiceReference<Broker> reference, final Broker service) {
- if (session != null) {
+ public void modifiedService(ServiceReference<Broker> reference, Broker service) {
+ if (session != null)
session.close();
- }
Broker broker = bundleContext.getService(reference);
session = broker.registerProvider(this, bundleContext);
}
@Override
- public void removedService(final ServiceReference<Broker> reference, final Broker service) {
+ public void removedService(ServiceReference<Broker> reference, Broker service) {
bundleContext.ungetService(reference);
}
}
import javax.ws.rs.core.Response;
/**
- * This service generates swagger
- * (See <a href="https://helloreverb.com/developers/swagger">https://helloreverb.com/developers/swagger</a>)
- * compliant documentation for RESTCONF APIs. The output of this is used by embedded Swagger UI.
+ * This service generates swagger (See <a
+ * href="https://helloreverb.com/developers/swagger"
+ * >https://helloreverb.com/developers/swagger</a>) compliant documentation for
+ * RESTCONF APIs. The output of this is used by embedded Swagger UI.
*/
@Path("/")
public interface ApiDocService {
- /**
- * Generates index document for Swagger UI. This document lists out all modules with link to get APIs for
- * each module. The API for each module is served by <code> getDocByModule()</code> method.
- *
- * @param uriInfo
- * @return
- */
- @GET
- @Produces(MediaType.APPLICATION_JSON)
- public Response getRootDoc(@Context javax.ws.rs.core.UriInfo uriInfo);
-
- /**
- * Generates Swagger compliant document listing APIs for module.
- *
- * @param module
- * @param revision
- * @param uriInfo
- * @return
- */
- @GET
- @Path("/{module},{revision}")
- @Produces(MediaType.APPLICATION_JSON)
- public Response getDocByModule(@PathParam("module") String module,
- @PathParam("revision") String revision,
- @Context javax.ws.rs.core.UriInfo uriInfo);
-
- /**
- * Redirects to embedded swagger ui.
- *
- * @param uriInfo
- * @return
- */
- @GET
- @Path("/ui")
- @Produces(MediaType.TEXT_HTML)
- public Response getApiExplorer(@Context javax.ws.rs.core.UriInfo uriInfo);
+ /**
+ * Generates index document for Swagger UI. This document lists out all
+ * modules with link to get APIs for each module. The API for each module is
+ * served by <code> getDocByModule()</code> method.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getRootDoc(@Context javax.ws.rs.core.UriInfo uriInfo);
+
+ /**
+ * Generates Swagger compliant document listing APIs for module.
+ *
+ * @param module
+ * @param revision
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Path("/{module}({revision})")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getDocByModule(@PathParam("module") String module,
+ @PathParam("revision") String revision, @Context javax.ws.rs.core.UriInfo uriInfo);
+
+ /**
+ * Redirects to embedded swagger ui.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Path("/ui")
+ @Produces(MediaType.TEXT_HTML)
+ public Response getApiExplorer(@Context javax.ws.rs.core.UriInfo uriInfo);
+
+ /**
+ * Generates index document for Swagger UI. This document lists out all
+ * modules with link to get APIs for each module. The API for each module is
+ * served by <code> getDocByModule()</code> method.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Path("/mounts")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getListOfMounts(@Context javax.ws.rs.core.UriInfo uriInfo);
+
+ @GET
+ @Path("/mounts/{instance}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getMountRootDoc(@PathParam("instance") String instanceNum,
+ @Context javax.ws.rs.core.UriInfo uriInfo);
+
+ /**
+ * Generates Swagger compliant document listing APIs for module.
+ *
+ * @param module
+ * @param revision
+ * @param uriInfo
+ * @return
+ */
+ @GET
+ @Path("/mounts/{instance}/{module}({revision})")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getMountDocByModule(@PathParam("instance") String instanceNum,
+ @PathParam("module") String module, @PathParam("revision") String revision,
+ @Context javax.ws.rs.core.UriInfo uriInfo);
+
}
*/
package org.opendaylight.controller.sal.rest.doc.impl;
-import java.io.IOException;
-import java.net.URI;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
import javax.ws.rs.core.UriInfo;
-import org.json.JSONException;
-import org.json.JSONObject;
import org.opendaylight.controller.sal.core.api.model.SchemaService;
-import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder;
-import org.opendaylight.controller.sal.rest.doc.swagger.Api;
import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
-import org.opendaylight.controller.sal.rest.doc.swagger.Operation;
-import org.opendaylight.controller.sal.rest.doc.swagger.Parameter;
-import org.opendaylight.controller.sal.rest.doc.swagger.Resource;
import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
import com.google.common.base.Preconditions;
/**
- * This class gathers all yang defined {@link Module}s and generates Swagger compliant documentation.
+ * This class gathers all yang defined {@link Module}s and generates Swagger
+ * compliant documentation.
*/
-public class ApiDocGenerator {
+public class ApiDocGenerator extends BaseYangSwaggerGenerator {
- private static final Logger _logger = LoggerFactory.getLogger(ApiDocGenerator.class);
+ private static Logger _logger = LoggerFactory.getLogger(ApiDocGenerator.class);
private static final ApiDocGenerator INSTANCE = new ApiDocGenerator();
- private final ObjectMapper mapper = new ObjectMapper();
- private final ModelGenerator jsonConverter = new ModelGenerator();
-
private SchemaService schemaService;
- private static final String API_VERSION = "1.0.0";
- private static final String SWAGGER_VERSION = "1.2";
- private static final String RESTCONF_CONTEXT_ROOT = "restconf";
- private final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
-
- //For now its {@link HashMap}. It will be changed to thread-safe Map when schema change listener is implemented.
- private final Map<String, ApiDeclaration> MODULE_DOC_CACHE = new HashMap<String, ApiDeclaration>();
+ public ResourceList getResourceListing(UriInfo uriInfo) {
+ Preconditions.checkState(schemaService != null);
+ SchemaContext schemaContext = schemaService.getGlobalContext();
+ Preconditions.checkState(schemaContext != null);
+ return super.getResourceListing(uriInfo, schemaContext, "");
+ }
- private ApiDocGenerator(){
- mapper.registerModule(new JsonOrgModule());
- mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+ public ApiDeclaration getApiDeclaration(String module, String revision, UriInfo uriInfo) {
+ SchemaContext schemaContext = schemaService.getGlobalContext();
+ Preconditions.checkState(schemaContext != null);
+ return super.getApiDeclaration(module, revision, uriInfo, schemaContext, "");
}
/**
* Returns singleton instance
+ *
* @return
*/
public static ApiDocGenerator getInstance() {
*
* @param schemaService
*/
- public void setSchemaService(final SchemaService schemaService) {
+ public void setSchemaService(SchemaService schemaService) {
this.schemaService = schemaService;
}
- /**
- *
- * @param uriInfo
- * @return list of modules converted to swagger compliant resource list.
- */
- public ResourceList getResourceListing(final UriInfo uriInfo) {
-
- Preconditions.checkState(schemaService != null);
- SchemaContext schemaContext = schemaService.getGlobalContext();
- Preconditions.checkState(schemaContext != null);
-
- Set<Module> modules = schemaContext.getModules();
-
- ResourceList resourceList = new ResourceList();
- resourceList.setApiVersion(API_VERSION);
- resourceList.setSwaggerVersion(SWAGGER_VERSION);
-
- List<Resource> resources = new ArrayList<>(modules.size());
- _logger.info("Modules found [{}]", modules.size());
-
- for (Module module : modules) {
- Resource resource = new Resource();
- String revisionString = SIMPLE_DATE_FORMAT.format(module.getRevision());
-
- _logger.debug("Working on [{},{}]...", module.getName(), revisionString);
- ApiDeclaration doc = getApiDeclaration(module.getName(), revisionString, uriInfo);
-
- if (doc != null) {
- URI uri = uriInfo.getRequestUriBuilder().
- path(generateCacheKey(module.getName(), revisionString)).
- build();
-
- resource.setPath(uri.toASCIIString());
- resources.add(resource);
- } else {
- _logger.debug("Could not generate doc for {},{}", module.getName(), revisionString);
- }
- }
-
- resourceList.setApis(resources);
-
- return resourceList;
- }
-
- public ApiDeclaration getApiDeclaration(final String module, final String revision, final UriInfo uriInfo) {
-
- //Lookup cache
- String cacheKey = generateCacheKey(module, revision);
-
- if (MODULE_DOC_CACHE.containsKey(cacheKey)) {
- _logger.debug("Serving from cache for {}", cacheKey);
- return MODULE_DOC_CACHE.get(cacheKey);
- }
-
- Date rev = null;
- try {
- rev = SIMPLE_DATE_FORMAT.parse(revision);
- } catch (ParseException e) {
- throw new IllegalArgumentException(e);
- }
-
- SchemaContext schemaContext = schemaService.getGlobalContext();
- Preconditions.checkState(schemaContext != null);
-
- Module m = schemaContext.findModuleByName(module, rev);
- Preconditions.checkArgument(m != null, "Could not find module by name,revision: " + module + "," + revision);
-
- String basePath = new StringBuilder(uriInfo.getBaseUri().getScheme())
- .append("://")
- .append(uriInfo.getBaseUri().getHost())
- .append(":")
- .append(uriInfo.getBaseUri().getPort())
- .append("/")
- .append(RESTCONF_CONTEXT_ROOT)
- .toString();
-
- ApiDeclaration doc = getSwaggerDocSpec(m, basePath);
- MODULE_DOC_CACHE.put(cacheKey, doc);
- return doc;
- }
-
- public ApiDeclaration getSwaggerDocSpec(final Module m, final String basePath) {
- ApiDeclaration doc = new ApiDeclaration();
- doc.setApiVersion(API_VERSION);
- doc.setSwaggerVersion(SWAGGER_VERSION);
- doc.setBasePath(basePath);
- doc.setProduces(Arrays.asList("application/json", "application/xml"));
-
- List<Api> apis = new ArrayList<Api>();
-
- Set<DataSchemaNode> dataSchemaNodes = m.getChildNodes();
- _logger.debug("child nodes size [{}]", dataSchemaNodes.size());
- for (DataSchemaNode node : dataSchemaNodes) {
- if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) {
-
- _logger.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node.getQName().getLocalName());
-
- List<Parameter> pathParams = null;
- if (node.isConfiguration()) {
- pathParams = new ArrayList<Parameter>();
- String resourcePath = "/config/" + m.getName() + ":";
- addApis(node, apis, resourcePath, pathParams, true);
-
- }
-
- pathParams = new ArrayList<Parameter>();
- String resourcePath = "/operational/" + m.getName() + ":";
- addApis(node, apis, resourcePath, pathParams, false);
- }
- }
-
- Set<RpcDefinition> rpcs = m.getRpcs();
- for (RpcDefinition rpcDefinition : rpcs) {
- String resourcePath = "/operations/" + m.getName() + ":";
- addRpcs(rpcDefinition, apis, resourcePath);
-
- }
- _logger.debug("Number of APIs found [{}]", apis.size());
- doc.setApis(apis);
- JSONObject models = null;
-
- try {
- models = jsonConverter.convertToJsonSchema(m);
- doc.setModels(models);
- _logger.debug(mapper.writeValueAsString(doc));
- } catch (IOException | JSONException e) {
- e.printStackTrace();
- }
-
- return doc;
- }
-
- private String generateCacheKey(final Module m) {
- return generateCacheKey(m.getName(), SIMPLE_DATE_FORMAT.format(m.getRevision()));
- }
-
- private String generateCacheKey(final String module, final String revision) {
- return module + "," + revision;
- }
-
- private void addApis(final DataSchemaNode node,
- final List<Api> apis,
- final String parentPath,
- final List<Parameter> parentPathParams,
- final boolean addConfigApi) {
-
- Api api = new Api();
- List<Parameter> pathParams = new ArrayList<Parameter>(parentPathParams);
-
- String resourcePath = parentPath + createPath(node, pathParams) + "/";
- _logger.debug("Adding path: [{}]", resourcePath);
- api.setPath(resourcePath);
- api.setOperations(operations(node, pathParams, addConfigApi));
- apis.add(api);
- if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) {
- DataNodeContainer schemaNode = (DataNodeContainer) node;
- Set<DataSchemaNode> dataSchemaNodes = schemaNode.getChildNodes();
-
- for (DataSchemaNode childNode : dataSchemaNodes) {
- addApis(childNode, apis, resourcePath, pathParams, addConfigApi);
- }
- }
-
- }
-
- private void addRpcs(final RpcDefinition rpcDefn, final List<Api> apis, final String parentPath) {
- Api rpc = new Api();
- String resourcePath = parentPath + rpcDefn.getQName().getLocalName();
- rpc.setPath(resourcePath);
-
- Operation operationSpec = new Operation();
- operationSpec.setMethod("POST");
- operationSpec.setNotes(rpcDefn.getDescription());
- operationSpec.setNickname(rpcDefn.getQName().getLocalName());
- rpc.setOperations(Arrays.asList(operationSpec));
-
- apis.add(rpc);
- }
-
- /**
- * @param node
- * @param pathParams
- * @return
- */
- private List<Operation> operations(final DataSchemaNode node, final List<Parameter> pathParams, final boolean isConfig) {
- List<Operation> operations = new ArrayList<>();
-
- OperationBuilder.Get getBuilder = new OperationBuilder.Get(node);
- operations.add(getBuilder.pathParams(pathParams).build());
-
- if (isConfig) {
- OperationBuilder.Post postBuilder = new OperationBuilder.Post(node);
- operations.add(postBuilder.pathParams(pathParams).build());
-
- OperationBuilder.Put putBuilder = new OperationBuilder.Put(node);
- operations.add(putBuilder.pathParams(pathParams).build());
-
- OperationBuilder.Delete deleteBuilder = new OperationBuilder.Delete(node);
- operations.add(deleteBuilder.pathParams(pathParams).build());
- }
- return operations;
- }
-
- private String createPath(final DataSchemaNode schemaNode, final List<Parameter> pathParams) {
- ArrayList<LeafSchemaNode> pathListParams = new ArrayList<LeafSchemaNode>();
- StringBuilder path = new StringBuilder();
- QName _qName = schemaNode.getQName();
- String localName = _qName.getLocalName();
- path.append(localName);
-
- if ((schemaNode instanceof ListSchemaNode)) {
- final List<QName> listKeys = ((ListSchemaNode) schemaNode).getKeyDefinition();
- for (final QName listKey : listKeys) {
- {
- DataSchemaNode _dataChildByName = ((DataNodeContainer) schemaNode).getDataChildByName(listKey);
- pathListParams.add(((LeafSchemaNode) _dataChildByName));
-
- String pathParamIdentifier = new StringBuilder("/{").append(listKey.getLocalName()).append("}").toString();
- path.append(pathParamIdentifier);
-
- Parameter pathParam = new Parameter();
- pathParam.setName(listKey.getLocalName());
- pathParam.setDescription(_dataChildByName.getDescription());
- pathParam.setType("string");
- pathParam.setParamType("path");
-
- pathParams.add(pathParam);
- }
- }
- }
- return path.toString();
- }
-
}
*/
package org.opendaylight.controller.sal.rest.doc.impl;
-import org.opendaylight.controller.sal.rest.doc.api.ApiDocService;
-import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
-import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Map.Entry;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import org.json.JSONWriter;
+import org.opendaylight.controller.sal.rest.doc.api.ApiDocService;
+import org.opendaylight.controller.sal.rest.doc.mountpoints.MountPointSwagger;
+import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
+import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
+
/**
- * This service generates swagger
- * (See <a href="https://helloreverb.com/developers/swagger">https://helloreverb.com/developers/swagger</a>)
- * compliant documentation for RESTCONF APIs. The output of this is used by embedded Swagger UI.
+ * This service generates swagger (See <a
+ * href="https://helloreverb.com/developers/swagger"
+ * >https://helloreverb.com/developers/swagger</a>) compliant documentation for
+ * RESTCONF APIs. The output of this is used by embedded Swagger UI.
+ *
+ * NOTE: These API's need to be synchronized due to bug 1198. Thread access to
+ * the SchemaContext is not synchronized properly and thus you can end up with
+ * missing definitions without this synchronization. There are likely otherways
+ * to work around this limitation, but given that this API is a dev only tool
+ * and not dependent UI, this was the fastest work around.
+ *
*/
public class ApiDocServiceImpl implements ApiDocService {
- private static final ApiDocService INSTANCE = new ApiDocServiceImpl();
+ private static final ApiDocService INSTANCE = new ApiDocServiceImpl();
+
+ public static ApiDocService getInstance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Generates index document for Swagger UI. This document lists out all
+ * modules with link to get APIs for each module. The API for each module is
+ * served by <code> getDocByModule()</code> method.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @Override
+ public synchronized Response getRootDoc(UriInfo uriInfo) {
+ ApiDocGenerator generator = ApiDocGenerator.getInstance();
+ ResourceList rootDoc = generator.getResourceListing(uriInfo);
+
+ return Response.ok(rootDoc).build();
+ }
- public static ApiDocService getInstance(){
- return INSTANCE;
- }
+ /**
+ * Generates Swagger compliant document listing APIs for module.
+ *
+ * @param module
+ * @param revision
+ * @param uriInfo
+ * @return
+ */
+ @Override
+ public synchronized Response getDocByModule(String module, String revision, UriInfo uriInfo) {
+ ApiDocGenerator generator = ApiDocGenerator.getInstance();
- /**
- * Generates index document for Swagger UI. This document lists out all modules with link to get APIs for
- * each module. The API for each module is served by <code> getDocByModule()</code> method.
- *
- * @param uriInfo
- * @return
- */
- @Override
- public Response getRootDoc(UriInfo uriInfo) {
+ ApiDeclaration doc = generator.getApiDeclaration(module, revision, uriInfo);
+ return Response.ok(doc).build();
+ }
- ApiDocGenerator generator = ApiDocGenerator.getInstance();
- ResourceList rootDoc = generator.getResourceListing(uriInfo);
+ /**
+ * Redirects to embedded swagger ui.
+ *
+ * @param uriInfo
+ * @return
+ */
+ @Override
+ public synchronized Response getApiExplorer(UriInfo uriInfo) {
+ return Response
+ .seeOther(uriInfo.getBaseUriBuilder().path("../explorer/index.html").build())
+ .build();
+ }
- return Response.ok(rootDoc).build();
- }
+ @Override
+ public synchronized Response getListOfMounts(UriInfo uriInfo) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (OutputStreamWriter streamWriter = new OutputStreamWriter(baos)) {
+ JSONWriter writer = new JSONWriter(streamWriter);
+ writer.array();
+ for (Entry<String, Long> entry : MountPointSwagger.getInstance()
+ .getInstanceIdentifiers().entrySet()) {
+ writer.object();
+ writer.key("instance").value(entry.getKey());
+ writer.key("id").value(entry.getValue());
+ writer.endObject();
+ }
+ writer.endArray();
+ } catch (Exception e) {
+ return Response.status(500).entity(e.getMessage()).build();
+ }
+ return Response.status(200).entity(baos.toString()).build();
+ }
- /**
- * Generates Swagger compliant document listing APIs for module.
- *
- * @param module
- * @param revision
- * @param uriInfo
- * @return
- */
- @Override
- public Response getDocByModule(String module, String revision, UriInfo uriInfo) {
- ApiDocGenerator generator = ApiDocGenerator.getInstance();
+ @Override
+ public synchronized Response getMountRootDoc(String instanceNum, UriInfo uriInfo) {
+ ResourceList resourceList = MountPointSwagger.getInstance().getResourceList(uriInfo,
+ Long.parseLong(instanceNum));
+ return Response.ok(resourceList).build();
+ }
- ApiDeclaration doc = generator.getApiDeclaration(module, revision, uriInfo);
- return Response.ok(doc).build();
- }
+ @Override
+ public synchronized Response getMountDocByModule(String instanceNum, String module,
+ String revision, UriInfo uriInfo) {
+ ApiDeclaration api = MountPointSwagger.getInstance().getMountPointApi(uriInfo,
+ Long.parseLong(instanceNum), module, revision);
+ return Response.ok(api).build();
+ }
- /**
- * Redirects to embedded swagger ui.
- *
- * @param uriInfo
- * @return
- */
- @Override
- public Response getApiExplorer(UriInfo uriInfo) {
- return Response.seeOther(uriInfo.getBaseUriBuilder().path("../explorer/index.html").build()).build();
- }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications 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.sal.rest.doc.impl;
+
+import java.io.IOException;
+import java.net.URI;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder;
+import org.opendaylight.controller.sal.rest.doc.swagger.Api;
+import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
+import org.opendaylight.controller.sal.rest.doc.swagger.Operation;
+import org.opendaylight.controller.sal.rest.doc.swagger.Parameter;
+import org.opendaylight.controller.sal.rest.doc.swagger.Resource;
+import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
+import com.google.common.base.Preconditions;
+
+public class BaseYangSwaggerGenerator {
+
+ private static Logger _logger = LoggerFactory.getLogger(BaseYangSwaggerGenerator.class);
+
+ protected static final String API_VERSION = "1.0.0";
+ protected static final String SWAGGER_VERSION = "1.2";
+ protected static final String RESTCONF_CONTEXT_ROOT = "restconf";
+ protected final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+ private final ModelGenerator jsonConverter = new ModelGenerator();
+
+ // private Map<String, ApiDeclaration> MODULE_DOC_CACHE = new HashMap<>()
+ private final ObjectMapper mapper = new ObjectMapper();
+
+ protected BaseYangSwaggerGenerator() {
+ mapper.registerModule(new JsonOrgModule());
+ mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+ }
+
+ /**
+ *
+ * @param uriInfo
+ * @param operType
+ * @return list of modules converted to swagger compliant resource list.
+ */
+ public ResourceList getResourceListing(UriInfo uriInfo, SchemaContext schemaContext,
+ String context) {
+
+ ResourceList resourceList = createResourceList();
+
+ Set<Module> modules = getSortedModules(schemaContext);
+
+ List<Resource> resources = new ArrayList<>(modules.size());
+
+ _logger.info("Modules found [{}]", modules.size());
+
+ for (Module module : modules) {
+ String revisionString = SIMPLE_DATE_FORMAT.format(module.getRevision());
+
+ Resource resource = new Resource();
+ _logger.debug("Working on [{},{}]...", module.getName(), revisionString);
+ ApiDeclaration doc = getApiDeclaration(module.getName(), revisionString, uriInfo,
+ schemaContext, context);
+
+ if (doc != null) {
+ resource.setPath(generatePath(uriInfo, module.getName(), revisionString));
+ resources.add(resource);
+ } else {
+ _logger.debug("Could not generate doc for {},{}", module.getName(), revisionString);
+ }
+ }
+
+ resourceList.setApis(resources);
+
+ return resourceList;
+ }
+
+ protected ResourceList createResourceList() {
+ ResourceList resourceList = new ResourceList();
+ resourceList.setApiVersion(API_VERSION);
+ resourceList.setSwaggerVersion(SWAGGER_VERSION);
+ return resourceList;
+ }
+
+ protected String generatePath(UriInfo uriInfo, String name, String revision) {
+ URI uri = uriInfo.getRequestUriBuilder().path(generateCacheKey(name, revision)).build();
+ return uri.toASCIIString();
+ }
+
+ public ApiDeclaration getApiDeclaration(String module, String revision, UriInfo uriInfo,
+ SchemaContext schemaContext, String context) {
+ Date rev = null;
+ try {
+ rev = SIMPLE_DATE_FORMAT.parse(revision);
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e);
+ }
+ Module m = schemaContext.findModuleByName(module, rev);
+ Preconditions.checkArgument(m != null, "Could not find module by name,revision: " + module
+ + "," + revision);
+
+ return getApiDeclaration(m, rev, uriInfo, schemaContext, context);
+ }
+
+ public ApiDeclaration getApiDeclaration(Module module, Date revision, UriInfo uriInfo,
+ SchemaContext schemaContext, String context) {
+ String basePath = createBasePathFromUriInfo(uriInfo);
+
+ ApiDeclaration doc = getSwaggerDocSpec(module, basePath, context);
+ if (doc != null) {
+ return doc;
+ }
+ return null;
+ }
+
+ protected String createBasePathFromUriInfo(UriInfo uriInfo) {
+ String portPart = "";
+ int port = uriInfo.getBaseUri().getPort();
+ if (port != -1) {
+ portPart = ":" + port;
+ }
+ String basePath = new StringBuilder(uriInfo.getBaseUri().getScheme()).append("://")
+ .append(uriInfo.getBaseUri().getHost()).append(portPart).append("/")
+ .append(RESTCONF_CONTEXT_ROOT).toString();
+ return basePath;
+ }
+
+ public ApiDeclaration getSwaggerDocSpec(Module m, String basePath, String context) {
+ ApiDeclaration doc = createApiDeclaration(basePath);
+
+ List<Api> apis = new ArrayList<Api>();
+
+ Set<DataSchemaNode> dataSchemaNodes = m.getChildNodes();
+ _logger.debug("child nodes size [{}]", dataSchemaNodes.size());
+ for (DataSchemaNode node : dataSchemaNodes) {
+ if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) {
+
+ _logger.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node
+ .getQName().getLocalName());
+
+ List<Parameter> pathParams = new ArrayList<Parameter>();
+ String resourcePath = getDataStorePath("/config/", context) + m.getName() + ":";
+ addApis(node, apis, resourcePath, pathParams, true);
+
+ pathParams = new ArrayList<Parameter>();
+ resourcePath = getDataStorePath("/operational/", context) + m.getName() + ":";
+ addApis(node, apis, resourcePath, pathParams, false);
+ }
+
+ Set<RpcDefinition> rpcs = m.getRpcs();
+ for (RpcDefinition rpcDefinition : rpcs) {
+ String resourcePath = getDataStorePath("/operations/", context) + m.getName() + ":";
+ addRpcs(rpcDefinition, apis, resourcePath);
+ }
+ }
+
+ _logger.debug("Number of APIs found [{}]", apis.size());
+
+ if (!apis.isEmpty()) {
+ doc.setApis(apis);
+ JSONObject models = null;
+
+ try {
+ models = jsonConverter.convertToJsonSchema(m);
+ doc.setModels(models);
+ if (_logger.isDebugEnabled()) {
+ _logger.debug(mapper.writeValueAsString(doc));
+ }
+ } catch (IOException | JSONException e) {
+ e.printStackTrace();
+ }
+
+ return doc;
+ }
+ return null;
+ }
+
+ protected ApiDeclaration createApiDeclaration(String basePath) {
+ ApiDeclaration doc = new ApiDeclaration();
+ doc.setApiVersion(API_VERSION);
+ doc.setSwaggerVersion(SWAGGER_VERSION);
+ doc.setBasePath(basePath);
+ doc.setProduces(Arrays.asList("application/json", "application/xml"));
+ return doc;
+ }
+
+ protected String getDataStorePath(String dataStore, String context) {
+ return dataStore + context;
+ }
+
+ private String generateCacheKey(Module m) {
+ return generateCacheKey(m.getName(), SIMPLE_DATE_FORMAT.format(m.getRevision()));
+ }
+
+ private String generateCacheKey(String module, String revision) {
+ return module + "(" + revision + ")";
+ }
+
+ private void addApis(DataSchemaNode node, List<Api> apis, String parentPath,
+ List<Parameter> parentPathParams, boolean addConfigApi) {
+
+ Api api = new Api();
+ List<Parameter> pathParams = new ArrayList<Parameter>(parentPathParams);
+
+ String resourcePath = parentPath + createPath(node, pathParams) + "/";
+ _logger.debug("Adding path: [{}]", resourcePath);
+ api.setPath(resourcePath);
+ api.setOperations(operations(node, pathParams, addConfigApi));
+ apis.add(api);
+ if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) {
+ DataNodeContainer schemaNode = (DataNodeContainer) node;
+ Set<DataSchemaNode> dataSchemaNodes = schemaNode.getChildNodes();
+
+ for (DataSchemaNode childNode : dataSchemaNodes) {
+ // We don't support going to leaf nodes today. Only lists and
+ // containers.
+ if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) {
+ // keep config and operation attributes separate.
+ if (childNode.isConfiguration() == addConfigApi) {
+ addApis(childNode, apis, resourcePath, pathParams, addConfigApi);
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * @param node
+ * @param pathParams
+ * @return
+ */
+ private List<Operation> operations(DataSchemaNode node, List<Parameter> pathParams,
+ boolean isConfig) {
+ List<Operation> operations = new ArrayList<>();
+
+ OperationBuilder.Get getBuilder = new OperationBuilder.Get(node, isConfig);
+ operations.add(getBuilder.pathParams(pathParams).build());
+
+ if (isConfig) {
+ OperationBuilder.Post postBuilder = new OperationBuilder.Post(node);
+ operations.add(postBuilder.pathParams(pathParams).build());
+
+ OperationBuilder.Put putBuilder = new OperationBuilder.Put(node);
+ operations.add(putBuilder.pathParams(pathParams).build());
+
+ OperationBuilder.Delete deleteBuilder = new OperationBuilder.Delete(node);
+ operations.add(deleteBuilder.pathParams(pathParams).build());
+ }
+ return operations;
+ }
+
+ private String createPath(final DataSchemaNode schemaNode, List<Parameter> pathParams) {
+ ArrayList<LeafSchemaNode> pathListParams = new ArrayList<LeafSchemaNode>();
+ StringBuilder path = new StringBuilder();
+ QName _qName = schemaNode.getQName();
+ String localName = _qName.getLocalName();
+ path.append(localName);
+
+ if ((schemaNode instanceof ListSchemaNode)) {
+ final List<QName> listKeys = ((ListSchemaNode) schemaNode).getKeyDefinition();
+ for (final QName listKey : listKeys) {
+ {
+ DataSchemaNode _dataChildByName = ((DataNodeContainer) schemaNode)
+ .getDataChildByName(listKey);
+ pathListParams.add(((LeafSchemaNode) _dataChildByName));
+
+ String pathParamIdentifier = new StringBuilder("/{")
+ .append(listKey.getLocalName()).append("}").toString();
+ path.append(pathParamIdentifier);
+
+ Parameter pathParam = new Parameter();
+ pathParam.setName(listKey.getLocalName());
+ pathParam.setDescription(_dataChildByName.getDescription());
+ pathParam.setType("string");
+ pathParam.setParamType("path");
+
+ pathParams.add(pathParam);
+ }
+ }
+ }
+ return path.toString();
+ }
+
+ protected void addRpcs(RpcDefinition rpcDefn, List<Api> apis, String parentPath) {
+ Api rpc = new Api();
+ String resourcePath = parentPath + rpcDefn.getQName().getLocalName();
+ rpc.setPath(resourcePath);
+
+ Operation operationSpec = new Operation();
+ operationSpec.setMethod("POST");
+ operationSpec.setNotes(rpcDefn.getDescription());
+ operationSpec.setNickname(rpcDefn.getQName().getLocalName());
+ if (rpcDefn.getOutput() != null) {
+ operationSpec.setType("(" + rpcDefn.getQName().getLocalName() + ")output");
+ }
+ if (rpcDefn.getInput() != null) {
+ Parameter payload = new Parameter();
+ payload.setParamType("body");
+ payload.setType("(" + rpcDefn.getQName().getLocalName() + ")input");
+ operationSpec.setParameters(Collections.singletonList(payload));
+ }
+
+ rpc.setOperations(Arrays.asList(operationSpec));
+
+ apis.add(rpc);
+ }
+
+ protected SortedSet<Module> getSortedModules(SchemaContext schemaContext) {
+ if (schemaContext == null) {
+ return new TreeSet<>();
+ }
+
+ Set<Module> modules = schemaContext.getModules();
+
+ SortedSet<Module> sortedModules = new TreeSet<>(new Comparator<Module>() {
+ @Override
+ public int compare(Module o1, Module o2) {
+ int result = o1.getName().compareTo(o2.getName());
+ if (result == 0) {
+ result = o1.getRevision().compareTo(o2.getRevision());
+ }
+ if (result == 0) {
+ result = o1.getNamespace().compareTo(o2.getNamespace());
+ }
+ return result;
+ }
+ });
+ for (Module m : modules) {
+ if (m != null) {
+ sortedModules.add(m);
+ }
+ }
+ return sortedModules;
+ }
+}
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder;
import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
*/
public class ModelGenerator {
- private static final Logger _logger = LoggerFactory.getLogger(ModelGenerator.class);
+ private static Logger _logger = LoggerFactory.getLogger(ModelGenerator.class);
private static final String BASE_64 = "base64";
private static final String BINARY_ENCODING_KEY = "binaryEncoding";
private static final Map<Class<? extends TypeDefinition<?>>, String> YANG_TYPE_TO_JSON_TYPE_MAPPING;
static {
- Map<Class<? extends TypeDefinition<?>>, String> tempMap1 = new HashMap<Class<? extends TypeDefinition<?>>, String>(10);
- tempMap1.put(StringType.class , STRING);
- tempMap1.put(BooleanType.class , BOOLEAN);
- tempMap1.put(Int8.class , INTEGER);
- tempMap1.put(Int16.class , INTEGER);
- tempMap1.put(Int32.class , INTEGER);
- tempMap1.put(Int64.class , INTEGER);
- tempMap1.put(Uint16.class , INTEGER);
- tempMap1.put(Uint32.class , INTEGER);
- tempMap1.put(Uint64.class , INTEGER);
- tempMap1.put(Uint8.class , INTEGER);
- tempMap1.put(Decimal64.class , NUMBER);
- tempMap1.put(EnumerationType.class , ENUM);
- //TODO: Binary type
+ Map<Class<? extends TypeDefinition<?>>, String> tempMap1 = new HashMap<Class<? extends TypeDefinition<?>>, String>(
+ 10);
+ tempMap1.put(StringType.class, STRING);
+ tempMap1.put(BooleanType.class, BOOLEAN);
+ tempMap1.put(Int8.class, INTEGER);
+ tempMap1.put(Int16.class, INTEGER);
+ tempMap1.put(Int32.class, INTEGER);
+ tempMap1.put(Int64.class, INTEGER);
+ tempMap1.put(Uint16.class, INTEGER);
+ tempMap1.put(Uint32.class, INTEGER);
+ tempMap1.put(Uint64.class, INTEGER);
+ tempMap1.put(Uint8.class, INTEGER);
+ tempMap1.put(Decimal64.class, NUMBER);
+ tempMap1.put(EnumerationType.class, ENUM);
+ // TODO: Binary type
YANG_TYPE_TO_JSON_TYPE_MAPPING = Collections.unmodifiableMap(tempMap1);
}
- public ModelGenerator(){
+ public ModelGenerator() {
}
- public JSONObject convertToJsonSchema(final Module module) throws IOException, JSONException {
+ public JSONObject convertToJsonSchema(Module module) throws IOException, JSONException {
JSONObject models = new JSONObject();
processContainers(module, models);
processRPCs(module, models);
-
return models;
}
-
-
- private void processContainers(final Module module, final JSONObject models) throws IOException, JSONException {
+ private void processContainers(Module module, JSONObject models) throws IOException,
+ JSONException {
String moduleName = module.getName();
- Set<DataSchemaNode> childNodes = module.getChildNodes();
+ Set<DataSchemaNode> childNodes = module.getChildNodes();
- for(DataSchemaNode childNode : childNodes){
- JSONObject moduleJSON=null;
- String filename = childNode.getQName().getLocalName();
+ for (DataSchemaNode childNode : childNodes) {
+ JSONObject configModuleJSON = null;
+ JSONObject operationalModuleJSON = null;
+
+ String childNodeName = childNode.getQName().getLocalName();
/*
* For every container in the module
*/
- if(childNode instanceof ContainerSchemaNode) {
- moduleJSON = processContainer((ContainerSchemaNode)childNode, moduleName, true, models);
+ if (childNode instanceof ContainerSchemaNode) {
+ configModuleJSON = processContainer((ContainerSchemaNode) childNode, moduleName,
+ true, models, true);
+ operationalModuleJSON = processContainer((ContainerSchemaNode) childNode,
+ moduleName, true, models, false);
}
- if(moduleJSON!=null) {
- _logger.debug("Adding model for [{}]", filename);
- moduleJSON.put("id", filename);
- models.put(filename, moduleJSON);
+ if (configModuleJSON != null) {
+ _logger.debug("Adding model for [{}]", OperationBuilder.CONFIG + childNodeName);
+ configModuleJSON.put("id", OperationBuilder.CONFIG + childNodeName);
+ models.put(OperationBuilder.CONFIG + childNodeName, configModuleJSON);
+ }
+ if (operationalModuleJSON != null) {
+ _logger.debug("Adding model for [{}]", OperationBuilder.OPERATIONAL + childNodeName);
+ operationalModuleJSON.put("id", OperationBuilder.OPERATIONAL + childNodeName);
+ models.put(OperationBuilder.OPERATIONAL + childNodeName, operationalModuleJSON);
}
}
}
-
/**
- * Process the RPCs for a Module
- * Spits out a file each of the name <rpcName>-input.json
- * and <rpcName>-output.json for each RPC that contains
+ * Process the RPCs for a Module Spits out a file each of the name
+ * <rpcName>-input.json and <rpcName>-output.json for each RPC that contains
* input & output elements
*
* @param module
* @throws JSONException
* @throws IOException
*/
- private void processRPCs(final Module module, final JSONObject models) throws JSONException, IOException {
+ private void processRPCs(Module module, JSONObject models) throws JSONException, IOException {
- Set<RpcDefinition> rpcs = module.getRpcs();
+ Set<RpcDefinition> rpcs = module.getRpcs();
String moduleName = module.getName();
- for(RpcDefinition rpc: rpcs) {
+ for (RpcDefinition rpc : rpcs) {
ContainerSchemaNode input = rpc.getInput();
- if(input!=null) {
+ if (input != null) {
JSONObject inputJSON = processContainer(input, moduleName, true, models);
- String filename = rpc.getQName().getLocalName() + "-input";
+ String filename = "(" + rpc.getQName().getLocalName() + ")input";
inputJSON.put("id", filename);
- //writeToFile(filename, inputJSON.toString(2), moduleName);
+ // writeToFile(filename, inputJSON.toString(2), moduleName);
models.put(filename, inputJSON);
}
ContainerSchemaNode output = rpc.getOutput();
- if(output!=null) {
+ if (output != null) {
JSONObject outputJSON = processContainer(output, moduleName, true, models);
- String filename = rpc.getQName().getLocalName() + "-output";
+ String filename = "(" + rpc.getQName().getLocalName() + ")output";
outputJSON.put("id", filename);
models.put(filename, outputJSON);
}
}
}
-
/**
* Processes the container node and populates the moduleJSON
*
* @param container
* @param moduleName
+ * @param isConfig
* @throws JSONException
* @throws IOException
*/
- private JSONObject processContainer(final ContainerSchemaNode container, final String moduleName, final boolean addSchemaStmt, final JSONObject models) throws JSONException, IOException{
+ private JSONObject processContainer(ContainerSchemaNode container, String moduleName,
+ boolean addSchemaStmt, JSONObject models) throws JSONException, IOException {
+ return processContainer(container, moduleName, addSchemaStmt, models, (Boolean) null);
+ }
+
+ private JSONObject processContainer(ContainerSchemaNode container, String moduleName,
+ boolean addSchemaStmt, JSONObject models, Boolean isConfig) throws JSONException,
+ IOException {
JSONObject moduleJSON = getSchemaTemplate();
- if(addSchemaStmt) {
+ if (addSchemaStmt) {
moduleJSON = getSchemaTemplate();
} else {
moduleJSON = new JSONObject();
moduleJSON.put(DESCRIPTION_KEY, containerDescription);
Set<DataSchemaNode> containerChildren = container.getChildNodes();
- JSONObject properties = processChildren(containerChildren, moduleName, models);
+ JSONObject properties = processChildren(containerChildren, moduleName, models, isConfig);
moduleJSON.put(PROPERTIES_KEY, properties);
return moduleJSON;
}
+ private JSONObject processChildren(Set<DataSchemaNode> nodes, String moduleName,
+ JSONObject models) throws JSONException, IOException {
+ return processChildren(nodes, moduleName, models, null);
+ }
+
/**
* Processes the nodes
+ *
* @param nodes
* @param moduleName
+ * @param isConfig
* @return
* @throws JSONException
* @throws IOException
*/
- private JSONObject processChildren(final Set<DataSchemaNode> nodes, final String moduleName, final JSONObject models) throws JSONException, IOException {
+ private JSONObject processChildren(Set<DataSchemaNode> nodes, String moduleName,
+ JSONObject models, Boolean isConfig) throws JSONException, IOException {
JSONObject properties = new JSONObject();
- for(DataSchemaNode node : nodes){
- String name = node.getQName().getLocalName();
- JSONObject property = null;
- if(node instanceof LeafSchemaNode) {
- property = processLeafNode((LeafSchemaNode)node);
- } else if (node instanceof ListSchemaNode) {
- property = processListSchemaNode((ListSchemaNode)node, moduleName, models);
+ for (DataSchemaNode node : nodes) {
+ if (isConfig == null || node.isConfiguration() == isConfig) {
- } else if (node instanceof LeafListSchemaNode) {
- property = processLeafListNode((LeafListSchemaNode)node);
+ String name = node.getQName().getLocalName();
+ JSONObject property = null;
+ if (node instanceof LeafSchemaNode) {
+ property = processLeafNode((LeafSchemaNode) node);
+ } else if (node instanceof ListSchemaNode) {
+ property = processListSchemaNode((ListSchemaNode) node, moduleName, models);
- } else if (node instanceof ChoiceNode) {
- property = processChoiceNode((ChoiceNode)node, moduleName, models);
+ } else if (node instanceof LeafListSchemaNode) {
+ property = processLeafListNode((LeafListSchemaNode) node);
- } else if (node instanceof AnyXmlSchemaNode) {
- property = processAnyXMLNode((AnyXmlSchemaNode)node);
+ } else if (node instanceof ChoiceNode) {
+ property = processChoiceNode((ChoiceNode) node, moduleName, models);
- } else if (node instanceof ContainerSchemaNode) {
- property = processContainer((ContainerSchemaNode)node, moduleName, false, models);
+ } else if (node instanceof AnyXmlSchemaNode) {
+ property = processAnyXMLNode((AnyXmlSchemaNode) node);
- } else {
- throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
- }
+ } else if (node instanceof ContainerSchemaNode) {
+ property = processContainer((ContainerSchemaNode) node, moduleName, false,
+ models, isConfig);
+
+ } else {
+ throw new IllegalArgumentException("Unknown DataSchemaNode type: "
+ + node.getClass());
+ }
- property.putOpt(DESCRIPTION_KEY, node.getDescription());
- properties.put(name, property);
+ property.putOpt(DESCRIPTION_KEY, node.getDescription());
+ properties.put(name, property);
+ }
}
return properties;
}
* @param listNode
* @throws JSONException
*/
- private JSONObject processLeafListNode(final LeafListSchemaNode listNode) throws JSONException {
+ private JSONObject processLeafListNode(LeafListSchemaNode listNode) throws JSONException {
JSONObject props = new JSONObject();
props.put(TYPE_KEY, ARRAY_TYPE);
* @throws JSONException
* @throws IOException
*/
- private JSONObject processChoiceNode(final ChoiceNode choiceNode, final String moduleName, final JSONObject models) throws JSONException, IOException {
+ private JSONObject processChoiceNode(ChoiceNode choiceNode, String moduleName, JSONObject models)
+ throws JSONException, IOException {
Set<ChoiceCaseNode> cases = choiceNode.getCases();
JSONArray choiceProps = new JSONArray();
- for(ChoiceCaseNode choiceCase: cases) {
+ for (ChoiceCaseNode choiceCase : cases) {
String choiceName = choiceCase.getQName().getLocalName();
JSONObject choiceProp = processChildren(choiceCase.getChildNodes(), moduleName, models);
JSONObject choiceObj = new JSONObject();
return oneOfProps;
}
-
/**
*
* @param constraints
* @param props
* @throws JSONException
*/
- private void processConstraints(final ConstraintDefinition constraints, final JSONObject props) throws JSONException {
+ private void processConstraints(ConstraintDefinition constraints, JSONObject props)
+ throws JSONException {
boolean isMandatory = constraints.isMandatory();
props.put(REQUIRED_KEY, isMandatory);
Integer minElements = constraints.getMinElements();
Integer maxElements = constraints.getMaxElements();
- if(minElements !=null) {
+ if (minElements != null) {
props.put(MIN_ITEMS, minElements);
}
- if(maxElements !=null) {
+ if (maxElements != null) {
props.put(MAX_ITEMS, maxElements);
}
}
/**
* Parses a ListSchema node.
*
- * Due to a limitation of the RAML--->JAX-RS tool, sub-properties
- * must be in a separate JSON schema file. Hence, we have to write
- * some properties to a new file, while continuing to process the rest.
+ * Due to a limitation of the RAML--->JAX-RS tool, sub-properties must be in
+ * a separate JSON schema file. Hence, we have to write some properties to a
+ * new file, while continuing to process the rest.
*
* @param listNode
* @param moduleName
* @throws JSONException
* @throws IOException
*/
- private JSONObject processListSchemaNode(final ListSchemaNode listNode, final String moduleName, final JSONObject models) throws JSONException, IOException {
+ private JSONObject processListSchemaNode(ListSchemaNode listNode, String moduleName,
+ JSONObject models) throws JSONException, IOException {
Set<DataSchemaNode> listChildren = listNode.getChildNodes();
String fileName = listNode.getQName().getLocalName();
childSchema.put(PROPERTIES_KEY, childSchemaProperties);
/*
- * Due to a limitation of the RAML--->JAX-RS tool, sub-properties
- * must be in a separate JSON schema file. Hence, we have to write
- * some properties to a new file, while continuing to process the rest.
+ * Due to a limitation of the RAML--->JAX-RS tool, sub-properties must
+ * be in a separate JSON schema file. Hence, we have to write some
+ * properties to a new file, while continuing to process the rest.
*/
- //writeToFile(fileName, childSchema.toString(2), moduleName);
+ // writeToFile(fileName, childSchema.toString(2), moduleName);
childSchema.put("id", fileName);
models.put(fileName, childSchema);
-
JSONObject listNodeProperties = new JSONObject();
listNodeProperties.put(TYPE_KEY, ARRAY_TYPE);
JSONObject items = new JSONObject();
- items.put(REF_KEY,fileName );
+ items.put(REF_KEY, fileName);
listNodeProperties.put(ITEMS_KEY, items);
return listNodeProperties;
* @return
* @throws JSONException
*/
- private JSONObject processLeafNode(final LeafSchemaNode leafNode) throws JSONException {
+ private JSONObject processLeafNode(LeafSchemaNode leafNode) throws JSONException {
JSONObject property = new JSONObject();
String leafDescription = leafNode.getDescription();
* @return
* @throws JSONException
*/
- private JSONObject processAnyXMLNode(final AnyXmlSchemaNode leafNode) throws JSONException {
+ private JSONObject processAnyXMLNode(AnyXmlSchemaNode leafNode) throws JSONException {
JSONObject property = new JSONObject();
String leafDescription = leafNode.getDescription();
* @param property
* @throws JSONException
*/
- private void processTypeDef(final TypeDefinition<?> leafTypeDef, final JSONObject property) throws JSONException {
+ private void processTypeDef(TypeDefinition<?> leafTypeDef, JSONObject property)
+ throws JSONException {
- if(leafTypeDef instanceof ExtendedType){
+ if (leafTypeDef instanceof ExtendedType) {
processExtendedType(leafTypeDef, property);
} else if (leafTypeDef instanceof EnumerationType) {
- processEnumType((EnumerationType)leafTypeDef, property);
+ processEnumType((EnumerationType) leafTypeDef, property);
} else if (leafTypeDef instanceof BitsTypeDefinition) {
- processBitsType((BitsTypeDefinition)leafTypeDef, property);
+ processBitsType((BitsTypeDefinition) leafTypeDef, property);
} else if (leafTypeDef instanceof UnionTypeDefinition) {
- processUnionType((UnionTypeDefinition)leafTypeDef, property);
+ processUnionType((UnionTypeDefinition) leafTypeDef, property);
} else if (leafTypeDef instanceof IdentityrefTypeDefinition) {
property.putOpt(TYPE_KEY, "object");
} else if (leafTypeDef instanceof BinaryTypeDefinition) {
- processBinaryType((BinaryTypeDefinition)leafTypeDef, property);
+ processBinaryType((BinaryTypeDefinition) leafTypeDef, property);
} else {
- //System.out.println("In else: " + leafTypeDef.getClass());
+ // System.out.println("In else: " + leafTypeDef.getClass());
String jsonType = YANG_TYPE_TO_JSON_TYPE_MAPPING.get(leafTypeDef.getClass());
- if(jsonType==null) {
+ if (jsonType == null) {
jsonType = "object";
}
property.putOpt(TYPE_KEY, jsonType);
* @param property
* @throws JSONException
*/
- private void processExtendedType(final TypeDefinition<?> leafTypeDef, final JSONObject property) throws JSONException {
+ private void processExtendedType(TypeDefinition<?> leafTypeDef, JSONObject property)
+ throws JSONException {
Object leafBaseType = leafTypeDef.getBaseType();
- if(leafBaseType instanceof ExtendedType){
- //recursively process an extended type until we hit a base type
- processExtendedType((TypeDefinition<?>)leafBaseType, property);
+ if (leafBaseType instanceof ExtendedType) {
+ // recursively process an extended type until we hit a base type
+ processExtendedType((TypeDefinition<?>) leafBaseType, property);
} else {
- List<LengthConstraint> lengthConstraints = ((ExtendedType) leafTypeDef).getLengthConstraints();
- for(LengthConstraint lengthConstraint: lengthConstraints) {
+ List<LengthConstraint> lengthConstraints = ((ExtendedType) leafTypeDef)
+ .getLengthConstraints();
+ for (LengthConstraint lengthConstraint : lengthConstraints) {
Number min = lengthConstraint.getMin();
Number max = lengthConstraint.getMax();
property.putOpt(MIN_LENGTH_KEY, min);
property.putOpt(MAX_LENGTH_KEY, max);
}
String jsonType = YANG_TYPE_TO_JSON_TYPE_MAPPING.get(leafBaseType.getClass());
- property.putOpt(TYPE_KEY,jsonType );
+ property.putOpt(TYPE_KEY, jsonType);
}
}
/*
- *
- */
- private void processBinaryType(final BinaryTypeDefinition binaryType, final JSONObject property) throws JSONException {
+ *
+ */
+ private void processBinaryType(BinaryTypeDefinition binaryType, JSONObject property)
+ throws JSONException {
property.put(TYPE_KEY, STRING);
JSONObject media = new JSONObject();
media.put(BINARY_ENCODING_KEY, BASE_64);
* @param property
* @throws JSONException
*/
- private void processEnumType(final EnumerationType enumLeafType, final JSONObject property) throws JSONException {
+ private void processEnumType(EnumerationType enumLeafType, JSONObject property)
+ throws JSONException {
List<EnumPair> enumPairs = enumLeafType.getValues();
List<String> enumNames = new ArrayList<String>();
- for(EnumPair enumPair: enumPairs) {
+ for (EnumPair enumPair : enumPairs) {
enumNames.add(enumPair.getName());
}
property.putOpt(ENUM, new JSONArray(enumNames));
* @param property
* @throws JSONException
*/
- private void processBitsType(final BitsTypeDefinition bitsType, final JSONObject property) throws JSONException{
+ private void processBitsType(BitsTypeDefinition bitsType, JSONObject property)
+ throws JSONException {
property.put(TYPE_KEY, ARRAY_TYPE);
property.put(MIN_ITEMS, 0);
property.put(UNIQUE_ITEMS_KEY, true);
JSONArray enumValues = new JSONArray();
List<Bit> bits = bitsType.getBits();
- for(Bit bit: bits) {
+ for (Bit bit : bits) {
enumValues.put(bit.getName());
}
JSONObject itemsValue = new JSONObject();
property.put(ITEMS_KEY, itemsValue);
}
-
/**
*
* @param unionType
* @param property
* @throws JSONException
*/
- private void processUnionType(final UnionTypeDefinition unionType, final JSONObject property) throws JSONException{
+ private void processUnionType(UnionTypeDefinition unionType, JSONObject property)
+ throws JSONException {
List<TypeDefinition<?>> unionTypes = unionType.getTypes();
JSONArray unionArray = new JSONArray();
- for(TypeDefinition<?> typeDef: unionTypes) {
+ for (TypeDefinition<?> typeDef : unionTypes) {
unionArray.put(YANG_TYPE_TO_JSON_TYPE_MAPPING.get(typeDef.getClass()));
}
property.put(TYPE_KEY, unionArray);
}
-
/**
- * Helper method to generate a pre-filled
- * JSON schema object.
+ * Helper method to generate a pre-filled JSON schema object.
+ *
* @return
* @throws JSONException
*/
*/
package org.opendaylight.controller.sal.rest.doc.jaxrs;
-import org.opendaylight.controller.sal.rest.doc.impl.ApiDocServiceImpl;
-
-import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
+import javax.ws.rs.core.Application;
+
+import org.opendaylight.controller.sal.rest.doc.impl.ApiDocServiceImpl;
+
public class ApiDocApplication extends Application {
- @Override
- public Set<Object> getSingletons() {
- Set<Object> singletons = new HashSet<>();
- singletons.add(ApiDocServiceImpl.getInstance());
- singletons.add(new JaxbContextResolver());
- return singletons;
- }
+ @Override
+ public Set<Object> getSingletons() {
+ Set<Object> singletons = new HashSet<>();
+ singletons.add(ApiDocServiceImpl.getInstance());
+ singletons.add(new JaxbContextResolver());
+ return singletons;
+ }
}
*/
package org.opendaylight.controller.sal.rest.doc.jaxrs;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
-import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
-
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
+import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
+
@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class JaxbContextResolver implements ContextResolver<ObjectMapper> {
- private ObjectMapper ctx;
+ private final ObjectMapper ctx;
- public JaxbContextResolver(){
- ctx = new ObjectMapper();
- ctx.registerModule(new JsonOrgModule());
- ctx.getSerializationConfig().withSerializationInclusion(JsonInclude.Include.ALWAYS);
- }
+ public JaxbContextResolver() {
+ ctx = new ObjectMapper();
+ ctx.registerModule(new JsonOrgModule());
+ ctx.getSerializationConfig().withSerializationInclusion(JsonInclude.Include.ALWAYS);
+ }
- @Override
- public ObjectMapper getContext(Class<?> aClass) {
+ @Override
+ public ObjectMapper getContext(Class<?> aClass) {
- if (ApiDeclaration.class.isAssignableFrom(aClass)){
- return ctx;
- }
+ if (ApiDeclaration.class.isAssignableFrom(aClass)) {
+ return ctx;
+ }
- return null;//must return null so that jax-rs can continue context search
- }
+ return null;// must return null so that jax-rs can continue context
+ // search
+ }
}
*/
package org.opendaylight.controller.sal.rest.doc.model.builder;
+import java.util.ArrayList;
+import java.util.List;
+
import org.opendaylight.controller.sal.rest.doc.swagger.Operation;
import org.opendaylight.controller.sal.rest.doc.swagger.Parameter;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import java.util.ArrayList;
-import java.util.List;
-
/**
*
*/
public final class OperationBuilder {
- /**
+ public static final String OPERATIONAL = "(operational)";
+ public static final String CONFIG = "(config)";
+
+ /**
*
*/
- public static class Get{
-
- protected Operation spec;
- protected DataSchemaNode schemaNode;
- private final String METHOD_NAME = "GET";
-
- public Get(DataSchemaNode node){
- this.schemaNode = node;
- spec = new Operation();
- spec.setMethod(METHOD_NAME);
- spec.setNickname(METHOD_NAME + "-" + node.getQName().getLocalName());
- spec.setType(node.getQName().getLocalName());
- spec.setNotes(node.getDescription());
+ public static class Get {
+
+ protected Operation spec;
+ protected DataSchemaNode schemaNode;
+ private final String METHOD_NAME = "GET";
+
+ public Get(DataSchemaNode node, boolean isConfig) {
+ this.schemaNode = node;
+ spec = new Operation();
+ spec.setMethod(METHOD_NAME);
+ spec.setNickname(METHOD_NAME + "-" + node.getQName().getLocalName());
+ spec.setType((isConfig ? CONFIG : OPERATIONAL) + node.getQName().getLocalName());
+ spec.setNotes(node.getDescription());
+ }
+
+ public Get pathParams(List<Parameter> params) {
+ List<Parameter> pathParameters = new ArrayList<>(params);
+ spec.setParameters(pathParameters);
+ return this;
+ }
+
+ public Operation build() {
+ return spec;
+ }
}
- public Get pathParams(List<Parameter> params){
- List<Parameter> pathParameters = new ArrayList<>(params);
- spec.setParameters(pathParameters);
- return this;
- }
-
- public Operation build(){
- return spec;
- }
- }
-
- /**
+ /**
*
*/
- public static class Put{
- protected Operation spec;
- protected DataSchemaNode schemaNode;
- private final String METHOD_NAME = "PUT";
-
- public Put(DataSchemaNode node){
- this.schemaNode = node;
- spec = new Operation();
- spec.setType(node.getQName().getLocalName());
- spec.setNotes(node.getDescription());
- }
-
- public Put pathParams(List<Parameter> params){
- List<Parameter> parameters = new ArrayList<>(params);
- Parameter payload = new Parameter();
- payload.setParamType("body");
- payload.setType(schemaNode.getQName().getLocalName());
- parameters.add(payload);
- spec.setParameters(parameters);
- return this;
- }
-
- public Operation build(){
- spec.setMethod(METHOD_NAME);
- spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
- return spec;
+ public static class Put {
+ protected Operation spec;
+ protected DataSchemaNode schemaNode;
+ private final String METHOD_NAME = "PUT";
+
+ public Put(DataSchemaNode node) {
+ this.schemaNode = node;
+ spec = new Operation();
+ spec.setType(CONFIG + node.getQName().getLocalName());
+ spec.setNotes(node.getDescription());
+ }
+
+ public Put pathParams(List<Parameter> params) {
+ List<Parameter> parameters = new ArrayList<>(params);
+ Parameter payload = new Parameter();
+ payload.setParamType("body");
+ payload.setType(CONFIG + schemaNode.getQName().getLocalName());
+ parameters.add(payload);
+ spec.setParameters(parameters);
+ return this;
+ }
+
+ public Operation build() {
+ spec.setMethod(METHOD_NAME);
+ spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
+ return spec;
+ }
}
- }
- /**
+ /**
*
*/
- public static final class Post extends Put{
+ public static final class Post extends Put {
- private final String METHOD_NAME = "POST";
+ private final String METHOD_NAME = "POST";
- public Post(DataSchemaNode node){
- super(node);
- }
+ public Post(DataSchemaNode node) {
+ super(node);
+ }
- public Operation build(){
- spec.setMethod(METHOD_NAME);
- spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
- return spec;
+ @Override
+ public Operation build() {
+ spec.setMethod(METHOD_NAME);
+ spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
+ return spec;
+ }
}
- }
- /**
+ /**
*
*/
- public static final class Delete extends Get {
- private final String METHOD_NAME = "DELETE";
-
- public Delete(DataSchemaNode node){
- super(node);
- }
-
- public Operation build(){
- spec.setMethod(METHOD_NAME);
- spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
- spec.setType(null);
- return spec;
+ public static final class Delete extends Get {
+ private final String METHOD_NAME = "DELETE";
+
+ public Delete(DataSchemaNode node) {
+ super(node, false);
+ }
+
+ @Override
+ public Operation build() {
+ spec.setMethod(METHOD_NAME);
+ spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName());
+ spec.setType(null);
+ return spec;
+ }
}
- }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 Brocade Communications 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.sal.rest.doc.mountpoints;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener;
+import org.opendaylight.controller.sal.rest.doc.impl.BaseYangSwaggerGenerator;
+import org.opendaylight.controller.sal.rest.doc.swagger.Api;
+import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration;
+import org.opendaylight.controller.sal.rest.doc.swagger.Operation;
+import org.opendaylight.controller.sal.rest.doc.swagger.Resource;
+import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class MountPointSwagger extends BaseYangSwaggerGenerator implements MountProvisionListener {
+
+ private static final String DATASTORES_REVISION = "-";
+ private static final String DATASTORES_LABEL = "Datastores";
+
+ private MountProvisionService mountService;
+ private final Map<InstanceIdentifier, Long> instanceIdToLongId = new TreeMap<>(
+ new Comparator<InstanceIdentifier>() {
+ @Override
+ public int compare(InstanceIdentifier o1, InstanceIdentifier o2) {
+ return o1.toString().compareToIgnoreCase(o2.toString());
+ }
+ });
+ private final Map<Long, InstanceIdentifier> longIdToInstanceId = new HashMap<>();
+ private final Object lock = new Object();
+
+ private final AtomicLong idKey = new AtomicLong(0);
+
+ private static AtomicReference<MountPointSwagger> selfRef = new AtomicReference<>();
+ private SchemaService globalSchema;
+
+ public Map<String, Long> getInstanceIdentifiers() {
+ Map<String, Long> urlToId = new HashMap<>();
+ synchronized (lock) {
+ SchemaContext context = globalSchema.getGlobalContext();
+ for (Entry<InstanceIdentifier, Long> entry : instanceIdToLongId.entrySet()) {
+ String modName = findModuleName(entry.getKey(), context);
+ urlToId.put(generateUrlPrefixFromInstanceID(entry.getKey(), modName),
+ entry.getValue());
+ }
+ }
+ return urlToId;
+ }
+
+ public void setGlobalSchema(SchemaService globalSchema) {
+ this.globalSchema = globalSchema;
+ }
+
+ private String findModuleName(InstanceIdentifier id, SchemaContext context) {
+ PathArgument rootQName = id.getPath().get(0);
+ for (Module mod : context.getModules()) {
+ if (mod.getDataChildByName(rootQName.getNodeType()) != null) {
+ return mod.getName();
+ }
+ }
+ return null;
+ }
+
+ private String generateUrlPrefixFromInstanceID(InstanceIdentifier key, String moduleName) {
+ List<PathArgument> path = key.getPath();
+ StringBuilder builder = new StringBuilder();
+ if (moduleName != null) {
+ builder.append(moduleName);
+ builder.append(":");
+ }
+ boolean first = true;
+ for (PathArgument arg : path) {
+
+ String name = arg.getNodeType().getLocalName();
+ if (first) {
+ first = false;
+ } else {
+ builder.append("/");
+ }
+ builder.append(name);
+ if (arg instanceof InstanceIdentifier.NodeIdentifierWithPredicates) {
+ NodeIdentifierWithPredicates nodeId = (NodeIdentifierWithPredicates) arg;
+ for (Entry<QName, Object> entry : nodeId.getKeyValues().entrySet()) {
+ builder.append("/").append(entry.getValue());
+ }
+ }
+ }
+
+ return builder.append("/").toString();
+ }
+
+ private String getYangMountUrl(InstanceIdentifier key) {
+ String modName = findModuleName(key, globalSchema.getGlobalContext());
+ return generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount/";
+ }
+
+ public ResourceList getResourceList(UriInfo uriInfo, Long id) {
+ InstanceIdentifier iid = getInstanceId(id);
+ if (iid == null) {
+ return null; // indicating not found.
+ }
+ SchemaContext context = getSchemaContext(iid);
+ String urlPrefix = getYangMountUrl(iid);
+ if (context == null) {
+ return createResourceList();
+ }
+ List<Resource> resources = new LinkedList<>();
+ Resource dataStores = new Resource();
+ dataStores.setDescription("Provides methods for accessing the data stores.");
+ dataStores.setPath(generatePath(uriInfo, DATASTORES_LABEL, DATASTORES_REVISION));
+ resources.add(dataStores);
+ ResourceList list = super.getResourceListing(uriInfo, context, urlPrefix);
+ resources.addAll(list.getApis());
+ list.setApis(resources);
+ return list;
+ }
+
+ private InstanceIdentifier getInstanceId(Long id) {
+ InstanceIdentifier instanceId;
+ synchronized (lock) {
+ instanceId = longIdToInstanceId.get(id);
+ }
+ return instanceId;
+ }
+
+ private SchemaContext getSchemaContext(InstanceIdentifier id) {
+
+ if (id == null) {
+ return null;
+ }
+
+ MountProvisionInstance mountPoint = mountService.getMountPoint(id);
+ if (mountPoint == null) {
+ return null;
+ }
+
+ SchemaContext context = mountPoint.getSchemaContext();
+ if (context == null) {
+ return null;
+ }
+ return context;
+ }
+
+ public ApiDeclaration getMountPointApi(UriInfo uriInfo, Long id, String module, String revision) {
+ InstanceIdentifier iid = getInstanceId(id);
+ SchemaContext context = getSchemaContext(iid);
+ String urlPrefix = getYangMountUrl(iid);
+ if (context == null) {
+ return null;
+ }
+
+ if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
+ return generateDataStoreApiDoc(uriInfo, urlPrefix);
+ }
+ return super.getApiDeclaration(module, revision, uriInfo, context, urlPrefix);
+ }
+
+ private ApiDeclaration generateDataStoreApiDoc(UriInfo uriInfo, String context) {
+
+ ApiDeclaration declaration = super.createApiDeclaration(createBasePathFromUriInfo(uriInfo));
+ List<Api> apis = new LinkedList<>();
+ apis.add(createGetApi("config",
+ "Queries the config (startup) datastore on the mounted hosted.", context));
+ apis.add(createGetApi("operational",
+ "Queries the operational (running) datastore on the mounted hosted.", context));
+ apis.add(createGetApi("operations",
+ "Queries the available operations (RPC calls) on the mounted hosted.", context));
+ declaration.setApis(apis);
+ return declaration;
+
+ }
+
+ private Api createGetApi(String datastore, String note, String context) {
+ Operation getConfig = new Operation();
+ getConfig.setMethod("GET");
+ getConfig.setNickname("GET " + datastore);
+ getConfig.setNotes(note);
+
+ Api api = new Api();
+ api.setPath(getDataStorePath("/" + datastore + "/", context));
+ api.setOperations(Collections.singletonList(getConfig));
+
+ return api;
+ }
+
+ public void setMountService(MountProvisionService mountService) {
+ this.mountService = mountService;
+ }
+
+ @Override
+ public void onMountPointCreated(InstanceIdentifier path) {
+ synchronized (lock) {
+ Long idLong = idKey.incrementAndGet();
+ instanceIdToLongId.put(path, idLong);
+ longIdToInstanceId.put(idLong, path);
+ }
+ }
+
+ @Override
+ public void onMountPointRemoved(InstanceIdentifier path) {
+ synchronized (lock) {
+ Long id = instanceIdToLongId.remove(path);
+ longIdToInstanceId.remove(id);
+ }
+ }
+
+ public static MountPointSwagger getInstance() {
+ MountPointSwagger swagger = selfRef.get();
+ if (swagger == null) {
+ selfRef.compareAndSet(null, new MountPointSwagger());
+ swagger = selfRef.get();
+ }
+ return swagger;
+ }
+
+}
import java.util.List;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api
+ * -object</a>)
*/
public class Api {
- private String path;
- private List<Operation> operations;
+ private String path;
+ private List<Operation> operations;
- public String getPath() {
- return path;
- }
+ public String getPath() {
+ return path;
+ }
- public void setPath(String path) {
- this.path = path;
- }
+ public void setPath(String path) {
+ this.path = path;
+ }
- public List<Operation> getOperations() {
- return operations;
- }
+ public List<Operation> getOperations() {
+ return operations;
+ }
- public void setOperations(List<Operation> operations) {
- this.operations = operations;
- }
+ public void setOperations(List<Operation> operations) {
+ this.operations = operations;
+ }
}
*/
package org.opendaylight.controller.sal.rest.doc.swagger;
-import org.json.JSONObject;
-
import java.util.List;
+import org.json.JSONObject;
+
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-
+ * declaration</a>)
*/
public class ApiDeclaration {
- private String apiVersion;
- private String swaggerVersion;
- private String basePath;
- private String resourcePath;
- private List<String> produces;
- private List<Api> apis;
- private JSONObject models;
-
- public JSONObject getModels() {
- return models;
- }
-
- public void setModels(JSONObject models) {
- this.models = models;
- }
-
- public String getApiVersion() {
- return apiVersion;
- }
-
- public void setApiVersion(String apiVersion) {
- this.apiVersion = apiVersion;
- }
-
- public String getSwaggerVersion() {
- return swaggerVersion;
- }
-
- public void setSwaggerVersion(String swaggerVersion) {
- this.swaggerVersion = swaggerVersion;
- }
-
- public String getBasePath() {
- return basePath;
- }
-
- public void setBasePath(String basePath) {
- this.basePath = basePath;
- }
-
- public String getResourcePath() {
- return resourcePath;
- }
-
- public void setResourcePath(String resourcePath) {
- this.resourcePath = resourcePath;
- }
-
- public List<String> getProduces() {
- return produces;
- }
-
- public void setProduces(List<String> produces) {
- this.produces = produces;
- }
-
- public List<Api> getApis() {
- return apis;
- }
-
- public void setApis(List<Api> apis) {
- this.apis = apis;
- }
-
- public boolean hasApi(){
- return (apis != null && !apis.isEmpty());
- }
-
- public boolean hasModel(){
- return (models != null && models.length() > 0);
- }
+ private String apiVersion;
+ private String swaggerVersion;
+ private String basePath;
+ private String resourcePath;
+ private List<String> produces;
+ private List<Api> apis;
+ private JSONObject models;
+
+ public JSONObject getModels() {
+ return models;
+ }
+
+ public void setModels(JSONObject models) {
+ this.models = models;
+ }
+
+ public String getApiVersion() {
+ return apiVersion;
+ }
+
+ public void setApiVersion(String apiVersion) {
+ this.apiVersion = apiVersion;
+ }
+
+ public String getSwaggerVersion() {
+ return swaggerVersion;
+ }
+
+ public void setSwaggerVersion(String swaggerVersion) {
+ this.swaggerVersion = swaggerVersion;
+ }
+
+ public String getBasePath() {
+ return basePath;
+ }
+
+ public void setBasePath(String basePath) {
+ this.basePath = basePath;
+ }
+
+ public String getResourcePath() {
+ return resourcePath;
+ }
+
+ public void setResourcePath(String resourcePath) {
+ this.resourcePath = resourcePath;
+ }
+
+ public List<String> getProduces() {
+ return produces;
+ }
+
+ public void setProduces(List<String> produces) {
+ this.produces = produces;
+ }
+
+ public List<Api> getApis() {
+ return apis;
+ }
+
+ public void setApis(List<Api> apis) {
+ this.apis = apis;
+ }
+
+ public boolean hasApi() {
+ return (apis != null && !apis.isEmpty());
+ }
+
+ public boolean hasModel() {
+ return (models != null && models.length() > 0);
+ }
}
import java.util.List;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-
+ * operation-object</a>)
*/
public class Operation {
- private String method;
- private String summary;
- private String notes;
- private String type;
- private String nickname;
- private List<String> consumes;
- private List<Parameter> parameters;
- private List<ResponseMessage> responseMessages;
-
- public String getMethod() {
- return method;
- }
-
- public void setMethod(String method) {
- this.method = method;
- }
-
- public String getSummary() {
- return summary;
- }
-
- public void setSummary(String summary) {
- this.summary = summary;
- }
-
- public String getNotes() {
- return notes;
- }
-
- public void setNotes(String notes) {
- this.notes = notes;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public String getNickname() {
- return nickname;
- }
-
- public void setNickname(String nickname) {
- this.nickname = nickname;
- }
-
- public List<String> getConsumes() {
- return consumes;
- }
-
- public void setConsumes(List<String> consumes) {
- this.consumes = consumes;
- }
-
- public List<Parameter> getParameters() {
- return parameters;
- }
-
- public void setParameters(List<Parameter> parameters) {
- this.parameters = parameters;
- }
-
- public List<ResponseMessage> getResponseMessages() {
- return responseMessages;
- }
-
- public void setResponseMessages(List<ResponseMessage> responseMessages) {
- this.responseMessages = responseMessages;
- }
+ private String method;
+ private String summary;
+ private String notes;
+ private String type;
+ private String nickname;
+ private List<String> consumes;
+ private List<Parameter> parameters;
+ private List<ResponseMessage> responseMessages;
+
+ public String getMethod() {
+ return method;
+ }
+
+ public void setMethod(String method) {
+ this.method = method;
+ }
+
+ public String getSummary() {
+ return summary;
+ }
+
+ public void setSummary(String summary) {
+ this.summary = summary;
+ }
+
+ public String getNotes() {
+ return notes;
+ }
+
+ public void setNotes(String notes) {
+ this.notes = notes;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getNickname() {
+ return nickname;
+ }
+
+ public void setNickname(String nickname) {
+ this.nickname = nickname;
+ }
+
+ public List<String> getConsumes() {
+ return consumes;
+ }
+
+ public void setConsumes(List<String> consumes) {
+ this.consumes = consumes;
+ }
+
+ public List<Parameter> getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List<Parameter> parameters) {
+ this.parameters = parameters;
+ }
+
+ public List<ResponseMessage> getResponseMessages() {
+ return responseMessages;
+ }
+
+ public void setResponseMessages(List<ResponseMessage> responseMessages) {
+ this.responseMessages = responseMessages;
+ }
}
package org.opendaylight.controller.sal.rest.doc.swagger;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524-parameter-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524-parameter-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524-parameter-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524-
+ * parameter-object</a>)
*/
public class Parameter {
- private String name;
- private String description;
- private boolean required;
- private String type;
- private String paramType;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- public boolean isRequired() {
- return required;
- }
-
- public void setRequired(boolean required) {
- this.required = required;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public String getParamType() {
- return paramType;
- }
-
- public void setParamType(String paramType) {
- this.paramType = paramType;
- }
+ private String name;
+ private String description;
+ private boolean required;
+ private String type;
+ private String paramType;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public boolean isRequired() {
+ return required;
+ }
+
+ public void setRequired(boolean required) {
+ this.required = required;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getParamType() {
+ return paramType;
+ }
+
+ public void setParamType(String paramType) {
+ this.paramType = paramType;
+ }
}
package org.opendaylight.controller.sal.rest.doc.swagger;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-resource-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-resource-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-resource-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-
+ * resource-object</a>)
*/
public class Resource {
- private String path;
- private String description;
+ private String path;
+ private String description;
- public String getPath() {
- return path;
- }
+ public String getPath() {
+ return path;
+ }
- public void setPath(String path) {
- this.path = path;
- }
+ public void setPath(String path) {
+ this.path = path;
+ }
- public String getDescription() {
- return description;
- }
+ public String getDescription() {
+ return description;
+ }
- public void setDescription(String description) {
- this.description = description;
- }
+ public void setDescription(String description) {
+ this.description = description;
+ }
}
import java.util.List;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-resource-listing">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-resource-listing</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-resource-listing"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-
+ * resource-listing</a>)
*/
public class ResourceList {
- private String apiVersion;
- private String swaggerVersion;
- private List<Resource> apis;
+ private String apiVersion;
+ private String swaggerVersion;
+ private List<Resource> apis;
- public String getApiVersion() {
- return apiVersion;
- }
+ public String getApiVersion() {
+ return apiVersion;
+ }
- public void setApiVersion(String apiVersion) {
- this.apiVersion = apiVersion;
- }
+ public void setApiVersion(String apiVersion) {
+ this.apiVersion = apiVersion;
+ }
- public String getSwaggerVersion() {
- return swaggerVersion;
- }
+ public String getSwaggerVersion() {
+ return swaggerVersion;
+ }
- public void setSwaggerVersion(String swaggerVersion) {
- this.swaggerVersion = swaggerVersion;
- }
+ public void setSwaggerVersion(String swaggerVersion) {
+ this.swaggerVersion = swaggerVersion;
+ }
- public List<Resource> getApis() {
- return apis;
- }
+ public List<Resource> getApis() {
+ return apis;
+ }
- public void setApis(List<Resource> apis) {
- this.apis = apis;
- }
+ public void setApis(List<Resource> apis) {
+ this.apis = apis;
+ }
}
package org.opendaylight.controller.sal.rest.doc.swagger;
/**
- * Implementation of swagger spec
- * (see <a href="https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525-response-message-object">
- * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525-response-message-object</a>)
+ * Implementation of swagger spec (see <a href=
+ * "https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525-response-message-object"
+ * > https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525-
+ * response-message-object</a>)
*/
public class ResponseMessage {
- private int code;
- private String message;
+ private int code;
+ private String message;
- public int getCode() {
- return code;
- }
+ public int getCode() {
+ return code;
+ }
- public void setCode(int code) {
- this.code = code;
- }
+ public void setCode(int code) {
+ this.code = code;
+ }
- public String getMessage() {
- return message;
- }
+ public String getMessage() {
+ return message;
+ }
- public void setMessage(String message) {
- this.message = message;
- }
+ public void setMessage(String message) {
+ this.message = message;
+ }
}
--- /dev/null
+/*! jQuery UI - v1.10.4 - 2014-06-03
+* http://jqueryui.com
+*