JsonParser parser = new JsonParser();
JsonElement rootElement = parser.parse(new InputStreamReader(entityStream));
+ if( rootElement.isJsonNull() )
+ {
+ //no content, so return null to indicate no input
+ return null;
+ }
+
if (!rootElement.isJsonObject()) {
throw new UnsupportedFormatException("Root element of Json has to be Object");
}
import static com.google.common.base.Preconditions.checkArgument;
+import java.io.BufferedInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Stack;
private final static XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
private XMLEventReader eventReader;
- public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException, UnsupportedFormatException {
+ public CompositeNodeWrapper read(InputStream entityStream) throws XMLStreamException,
+ UnsupportedFormatException,
+ IOException {
+ //Get an XML stream which can be marked, and reset, so we can check and see if there is
+ //any content being provided.
+ entityStream = getMarkableStream(entityStream);
+
+ if( isInputStreamEmpty( entityStream ) ) {
+ return null;
+ }
+
eventReader = xmlInputFactory.createXMLEventReader(entityStream);
if (eventReader.hasNext()) {
return root;
}
+ /**
+ * If the input stream is not markable, then it wraps the input stream with a buffered stream,
+ * which is mark able. That way we can check if the stream is empty safely.
+ * @param entityStream
+ * @return
+ */
+ private InputStream getMarkableStream(InputStream entityStream) {
+ if( !entityStream.markSupported() )
+ {
+ entityStream = new BufferedInputStream( entityStream );
+ }
+ return entityStream;
+ }
+
+ private boolean isInputStreamEmpty(InputStream entityStream)
+ throws IOException {
+ boolean isEmpty = false;
+ entityStream.mark( 1 );
+ if( entityStream.read() == -1 ){
+ isEmpty = true;
+ }
+ entityStream.reset();
+ return isEmpty;
+ }
+
private boolean isSimpleNodeEvent(final XMLEvent event) throws XMLStreamException {
checkArgument(event != null, "XML Event cannot be NULL!");
if (event.isStartElement()) {
*/
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.Iterables;
-import com.google.common.collect.Lists;
-
import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import org.opendaylight.controller.sal.core.api.mount.MountInstance;
import org.opendaylight.controller.sal.rest.api.Draft02;
import org.opendaylight.controller.sal.rest.api.RestconfService;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
import org.opendaylight.controller.sal.restconf.rpc.impl.BrokerRpcExecutor;
import org.opendaylight.controller.sal.restconf.rpc.impl.MountPointRpcExecutor;
import org.opendaylight.controller.sal.restconf.rpc.impl.RpcExecutor;
-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.EmptyNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
-import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
-import org.opendaylight.controller.sal.restconf.impl.NodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.RestCodec;
-import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper;
-import org.opendaylight.controller.sal.restconf.impl.StructuredData;
-import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
-import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
import org.opendaylight.controller.sal.streams.listeners.Notificator;
import org.opendaylight.controller.sal.streams.websockets.WebSocketServer;
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();
URI rpcNamespace = rpcName.getNamespace();
if (Objects.equal(rpcNamespace.toString(), SAL_REMOTE_NAMESPACE) &&
Objects.equal(rpcName.getLocalName(), SAL_REMOTE_RPC_SUBSRCIBE)) {
-
return invokeSalRemoteRpcSubscribeRPC(payload, rpc.getRpcDefinition());
}
+ validateInput( rpc.getRpcDefinition().getInput(), payload );
+
return callRpc(rpc, payload);
}
+ private void validateInput(DataSchemaNode inputSchema, CompositeNode payload) {
+ if( inputSchema != null && payload == null )
+ {
+ //expected a non null payload
+ throw new RestconfDocumentedException( "Input is required.",
+ ErrorType.PROTOCOL,
+ ErrorTag.MALFORMED_MESSAGE );
+ }
+ else if( inputSchema == null && payload != null )
+ {
+ //did not expect any input
+ throw new RestconfDocumentedException( "No input expected.",
+ ErrorType.PROTOCOL,
+ ErrorTag.MALFORMED_MESSAGE );
+ }
+ //else
+ //{
+ //TODO: Validate "mandatory" and "config" values here??? Or should those be
+ // validate in a more central location inside MD-SAL core.
+ //}
+ }
+
private StructuredData invokeSalRemoteRpcSubscribeRPC(final CompositeNode payload,
final RpcDefinition rpc) {
final CompositeNode value = this.normalizeNode(payload, rpc.getInput(), null);
throw new RestconfDocumentedException(
"Content must be empty.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
}
- final RpcExecutor rpc = resolveIdentifierInInvokeRpc(identifier);
- return callRpc(rpc, null);
+ return invokeRpc( identifier, (CompositeNode)null );
}
private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) {
@Override
public Response updateConfigurationData(final String identifier, final CompositeNode payload) {
final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
+
+ validateInput(iiWithData.getSchemaNode(), payload);
+
MountInstance mountPoint = iiWithData.getMountPoint();
final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
RpcResult<TransactionStatus> status = null;
@Override
public Response createConfigurationData(final String identifier, final CompositeNode payload) {
+ if( payload == null ) {
+ throw new RestconfDocumentedException( "Input is required.",
+ ErrorType.PROTOCOL,
+ ErrorTag.MALFORMED_MESSAGE );
+ }
+
URI payloadNS = this.namespace(payload);
if (payloadNS == null) {
throw new RestconfDocumentedException(
@Override
public Response createConfigurationData(final CompositeNode payload) {
+ if( payload == null ) {
+ throw new RestconfDocumentedException( "Input is required.",
+ ErrorType.PROTOCOL,
+ ErrorTag.MALFORMED_MESSAGE );
+ }
+
URI payloadNS = this.namespace(payload);
if (payloadNS == null) {
throw new RestconfDocumentedException(
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
}
+ @Test
+ public void testJsonBlankInput() throws Exception{
+ InputStream inputStream = new ByteArrayInputStream( "".getBytes() );
+ CompositeNode compositeNode =
+ JsonToCompositeNodeProvider.INSTANCE.readFrom(null, null, null, null, null, inputStream);
+ assertNull( compositeNode );
+ }
+
/**
* Tests whether namespace <b>stay unchanged</b> if concrete values are
* present in composite or simple node and if the method for update is
ListenableFuture<RpcResult<CompositeNode>> mockListener = mock( ListenableFuture.class );
when( mockListener.get() ).thenReturn( rpcResult );
- QName cancelToastQName = QName.create( "cancelToast" );
+ QName cancelToastQName = QName.create( "namespace", "2014-05-28", "cancelToast" );
RpcDefinition mockRpc = mock( RpcDefinition.class );
when( mockRpc.getQName() ).thenReturn( cancelToastQName );
mockCommitConfigurationDataPostMethod(TransactionStatus.FAILED);
assertEquals(500, post(uri, MediaType.APPLICATION_XML, xmlDataInterfaceAbsolutePath));
+
+ assertEquals( 400, post(uri, MediaType.APPLICATION_JSON, "" ));
}
@Test
assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData4));
uri = "/config/ietf-interfaces:interfaces/interface/0/yang-ext:mount/test-module:cont";
assertEquals(204, post(uri, Draft02.MediaTypes.DATA + XML, xmlData3));
+
+ assertEquals( 400, post(uri, MediaType.APPLICATION_JSON, "" ));
}
private void mockInvokeRpc(CompositeNode result, boolean sucessful) {
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
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.rest.impl.RestconfDocumentedExceptionMapper;
import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider;
import org.opendaylight.controller.sal.rest.impl.StructuredDataToXmlProvider;
import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
resourceConfig = resourceConfig.registerInstances(restconfImpl, StructuredDataToXmlProvider.INSTANCE,
StructuredDataToJsonProvider.INSTANCE, XmlToCompositeNodeProvider.INSTANCE,
JsonToCompositeNodeProvider.INSTANCE);
+ resourceConfig.registerClasses( RestconfDocumentedExceptionMapper.class );
return resourceConfig;
}
mockCommitConfigurationDataPutMethod(TransactionStatus.FAILED);
assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
+
+ assertEquals( 400, put(uri, MediaType.APPLICATION_JSON, "" ));
+ }
+
+ @Test
+ public void putConfigStatusCodesEmptyBody() throws UnsupportedEncodingException {
+ String uri = "/config/ietf-interfaces:interfaces/interface/eth0";
+ Response resp = target(uri).request( MediaType.APPLICATION_JSON).put(Entity.entity( "", MediaType.APPLICATION_JSON));
+ assertEquals( 400, put(uri, MediaType.APPLICATION_JSON, "" ));
}
@Test
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
import org.junit.BeforeClass;
import org.junit.Test;
import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider;
assertEquals("121", lf2.getValue());
}
+ @Test
+ public void testXmlBlankInput() throws Exception{
+ InputStream inputStream = new ByteArrayInputStream( "".getBytes() );
+ CompositeNode compositeNode =
+ XmlToCompositeNodeProvider.INSTANCE.readFrom(null, null, null, null, null, inputStream);
+
+ assertNull( compositeNode );
+ }
+
+ @Test
+ public void testXmlBlankInputUnmarkableStream() throws Exception{
+ InputStream inputStream = new ByteArrayInputStream( "".getBytes() ){
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+ };
+ CompositeNode compositeNode =
+ XmlToCompositeNodeProvider.INSTANCE.readFrom(null, null, null, null, null, inputStream);
+
+ assertNull( compositeNode );
+ }
+
}