Integrate non-blocking XML parser as Netty codec (#2806)

Motivation:
Provide non-blocking XML parser as Netty codec.

Modifications:
New codec implemented/extracted.

io.netty.handler.codec.xml.XmlDecoder decodes XML fed by Netty without blocking.

Result:
Non-blocking XML stream parsing.
This commit is contained in:
Marek Jelen 2014-08-27 11:46:51 +02:00 committed by Norman Maurer
parent 1e09409604
commit d3e7a0bcd0
22 changed files with 1330 additions and 0 deletions

View File

@ -195,3 +195,11 @@ can be obtained at:
* HOMEPAGE:
* http://logging.apache.org/log4j/
This product optionally depends on 'Aalto XML', an ultra-high performance
non-blocking XML processor, which can be obtained at:
* LICENSE:
* license/LICENSE.aalto-xml.txt (Apache License 2.0)
* HOMEPAGE:
* http://wiki.fasterxml.com/AaltoHome

48
codec-xml/pom.xml Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2014 The Netty Project
~
~ The Netty Project licenses this file to you under the Apache License,
~ version 2.0 (the "License"); you may not use this file except in compliance
~ with the License. You may obtain a copy of the License at:
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
~ License for the specific language governing permissions and limitations
~ under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.netty</groupId>
<artifactId>netty-parent</artifactId>
<version>4.1.0.Beta4-SNAPSHOT</version>
</parent>
<artifactId>netty-codec-xml</artifactId>
<packaging>jar</packaging>
<name>Netty/Codec/XML</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-codec</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-handler</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>aalto-xml</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,94 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* XML attributes, it is part of {@link XmlElement}
*/
public class XmlAttribute {
private final String type;
private final String name;
private final String prefix;
private final String namespace;
private final String value;
public XmlAttribute(String type, String name, String prefix, String namespace, String value) {
this.type = type;
this.name = name;
this.prefix = prefix;
this.namespace = namespace;
this.value = value;
}
public String type() {
return type;
}
public String name() {
return name;
}
public String prefix() {
return prefix;
}
public String namespace() {
return namespace;
}
public String value() {
return value;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
XmlAttribute that = (XmlAttribute) o;
if (!name.equals(that.name)) { return false; }
if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) { return false; }
if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { return false; }
if (type != null ? !type.equals(that.type) : that.type != null) { return false; }
if (value != null ? !value.equals(that.value) : that.value != null) { return false; }
return true;
}
@Override
public int hashCode() {
int result = type != null ? type.hashCode() : 0;
result = 31 * result + name.hashCode();
result = 31 * result + (prefix != null ? prefix.hashCode() : 0);
result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "XmlAttribute{" +
"type='" + type + '\'' +
", name='" + name + '\'' +
", prefix='" + prefix + '\'' +
", namespace='" + namespace + '\'' +
", value='" + value + '\'' +
'}';
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* XML CDATA ... <![CDATA[&lt;sender&gt;John Smith&lt;/sender&gt;]]>
*/
public class XmlCdata extends XmlContent {
public XmlCdata(String data) {
super(data);
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* XML characters, e.g. &lt;test&gt;characters&lt;/test&gt;, but not CDATA.
*/
public class XmlCharacters extends XmlContent {
public XmlCharacters(String data) {
super(data);
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* XML Comment
*/
public class XmlComment extends XmlContent {
public XmlComment(String data) {
super(data);
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* XML Content is base class for XML CDATA, Comments, Characters and Space
*/
public abstract class XmlContent {
private final String data;
protected XmlContent(String data) {
this.data = data;
}
public String data() {
return data;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
XmlContent that = (XmlContent) o;
if (data != null ? !data.equals(that.data) : that.data != null) { return false; }
return true;
}
@Override
public int hashCode() {
return data != null ? data.hashCode() : 0;
}
@Override
public String toString() {
return "XmlContent{" +
"data='" + data + '\'' +
'}';
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* DTD (Document Type Definition)
*/
public class XmlDTD {
private final String text;
public XmlDTD(String text) {
this.text = text;
}
public String text() {
return text;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
XmlDTD xmlDTD = (XmlDTD) o;
if (text != null ? !text.equals(xmlDTD.text) : xmlDTD.text != null) { return false; }
return true;
}
@Override
public int hashCode() {
return text != null ? text.hashCode() : 0;
}
@Override
public String toString() {
return "XmlDTD{" +
"text='" + text + '\'' +
'}';
}
}

View File

@ -0,0 +1,116 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
import com.fasterxml.aalto.AsyncInputFeeder;
import com.fasterxml.aalto.AsyncXMLInputFactory;
import com.fasterxml.aalto.AsyncXMLStreamReader;
import com.fasterxml.aalto.stax.InputFactoryImpl;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.util.List;
/**
* Async XML decoder based on <a href="https://github.com/FasterXML/aalto-xml">Aalto XML parser</a>.
*
* Parses the incoming data into one of XML messages defined in this package.
*/
public class XmlDecoder extends ByteToMessageDecoder {
private static final AsyncXMLInputFactory XML_INPUT_FACTORY = new InputFactoryImpl();
private static final XmlDocumentEnd XML_DOCUMENT_END = XmlDocumentEnd.INSTANCE;
private final AsyncXMLStreamReader streamReader = XML_INPUT_FACTORY.createAsyncXMLStreamReader();
private final AsyncInputFeeder streamFeeder = streamReader.getInputFeeder();
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
byte[] buffer = new byte[in.readableBytes()];
in.readBytes(buffer);
try {
streamFeeder.feedInput(buffer, 0, buffer.length);
} catch (XMLStreamException exception) {
in.skipBytes(in.readableBytes());
throw exception;
}
while (!streamFeeder.needMoreInput()) {
int type = streamReader.next();
switch (type) {
case XMLStreamConstants.START_DOCUMENT:
out.add(new XmlDocumentStart(streamReader.getEncoding(), streamReader.getVersion(),
streamReader.isStandalone(), streamReader.getCharacterEncodingScheme()));
break;
case XMLStreamConstants.END_DOCUMENT:
out.add(XML_DOCUMENT_END);
break;
case XMLStreamConstants.START_ELEMENT:
XmlElementStart elementStart = new XmlElementStart(streamReader.getLocalName(),
streamReader.getName().getNamespaceURI(), streamReader.getPrefix());
for (int x = 0; x < streamReader.getAttributeCount(); x++) {
XmlAttribute attribute = new XmlAttribute(streamReader.getAttributeType(x),
streamReader.getAttributeLocalName(x), streamReader.getAttributePrefix(x),
streamReader.getAttributeNamespace(x), streamReader.getAttributeValue(x));
elementStart.attributes().add(attribute);
}
for (int x = 0; x < streamReader.getNamespaceCount(); x++) {
XmlNamespace namespace = new XmlNamespace(streamReader.getNamespacePrefix(x),
streamReader.getNamespaceURI(x));
elementStart.namespaces().add(namespace);
}
out.add(elementStart);
break;
case XMLStreamConstants.END_ELEMENT:
XmlElementEnd elementEnd = new XmlElementEnd(streamReader.getLocalName(),
streamReader.getName().getNamespaceURI(), streamReader.getPrefix());
for (int x = 0; x < streamReader.getNamespaceCount(); x++) {
XmlNamespace namespace = new XmlNamespace(streamReader.getNamespacePrefix(x),
streamReader.getNamespaceURI(x));
elementEnd.namespaces().add(namespace);
}
out.add(elementEnd);
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
out.add(new XmlProcessingInstruction(streamReader.getPIData(), streamReader.getPITarget()));
break;
case XMLStreamConstants.CHARACTERS:
out.add(new XmlCharacters(streamReader.getText()));
break;
case XMLStreamConstants.COMMENT:
out.add(new XmlComment(streamReader.getText()));
break;
case XMLStreamConstants.SPACE:
out.add(new XmlSpace(streamReader.getText()));
break;
case XMLStreamConstants.ENTITY_REFERENCE:
out.add(new XmlEntityReference(streamReader.getLocalName(), streamReader.getText()));
break;
case XMLStreamConstants.DTD:
out.add(new XmlDTD(streamReader.getText()));
break;
case XMLStreamConstants.CDATA:
out.add(new XmlCdata(streamReader.getText()));
break;
}
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* End of XML document
*/
public final class XmlDocumentEnd {
public static final XmlDocumentEnd INSTANCE = new XmlDocumentEnd();
private XmlDocumentEnd() {
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* Beginning of the XML document ... i.e. XML header
*/
public class XmlDocumentStart {
private final String encoding;
private final String version;
private final boolean standalone;
private final String encodingScheme;
public XmlDocumentStart(String encoding, String version, boolean standalone, String encodingScheme) {
this.encoding = encoding;
this.version = version;
this.standalone = standalone;
this.encodingScheme = encodingScheme;
}
/** Return defined or guessed XML encoding **/
public String encoding() {
return encoding;
}
/** Return defined XML version or null **/
public String version() {
return version;
}
/** Return standalonity of the document **/
public boolean standalone() {
return standalone;
}
/** Return defined encoding or null **/
public String encodingScheme() {
return encodingScheme;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
XmlDocumentStart that = (XmlDocumentStart) o;
if (standalone != that.standalone) { return false; }
if (encoding != null ? !encoding.equals(that.encoding) : that.encoding != null) { return false; }
if (encodingScheme != null ? !encodingScheme.equals(that.encodingScheme) : that.encodingScheme != null) {
return false;
}
if (version != null ? !version.equals(that.version) : that.version != null) { return false; }
return true;
}
@Override
public int hashCode() {
int result = encoding != null ? encoding.hashCode() : 0;
result = 31 * result + (version != null ? version.hashCode() : 0);
result = 31 * result + (standalone ? 1 : 0);
result = 31 * result + (encodingScheme != null ? encodingScheme.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "XmlDocumentStart{" +
"encoding='" + encoding + '\'' +
", version='" + version + '\'' +
", standalone=" + standalone +
", encodingScheme='" + encodingScheme + '\'' +
'}';
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
import java.util.LinkedList;
import java.util.List;
/**
* Generic XML element in document, {@link XmlElementStart} represents open element and provides access to attributes,
* {@link XmlElementEnd} represents closing element.
*/
public abstract class XmlElement {
private final String name;
private final String namespace;
private final String prefix;
private final List<XmlNamespace> namespaces = new LinkedList<XmlNamespace>();
protected XmlElement(String name, String namespace, String prefix) {
this.name = name;
this.namespace = namespace;
this.prefix = prefix;
}
public String name() {
return name;
}
public String namespace() {
return namespace;
}
public String prefix() {
return prefix;
}
public List<XmlNamespace> namespaces() {
return namespaces;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
XmlElement that = (XmlElement) o;
if (!name.equals(that.name)) { return false; }
if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) { return false; }
if (namespaces != null ? !namespaces.equals(that.namespaces) : that.namespaces != null) { return false; }
if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { return false; }
return true;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
result = 31 * result + (prefix != null ? prefix.hashCode() : 0);
result = 31 * result + (namespaces != null ? namespaces.hashCode() : 0);
return result;
}
@Override
public String toString() {
return ", name='" + name + '\'' +
", namespace='" + namespace + '\'' +
", prefix='" + prefix + '\'' +
", namespaces=" + namespaces;
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* Specific {@link XmlElement} representing end of element.
*/
public class XmlElementEnd extends XmlElement {
public XmlElementEnd(String name, String namespace, String prefix) {
super(name, namespace, prefix);
}
@Override
public String toString() {
return "XmlElementStart{" +
super.toString() +
"} ";
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
import java.util.LinkedList;
import java.util.List;
/**
* Specific {@link XmlElement} representing beginning of element.
*/
public class XmlElementStart extends XmlElement {
private final List<XmlAttribute> attributes = new LinkedList<XmlAttribute>();
public XmlElementStart(String name, String namespace, String prefix) {
super(name, namespace, prefix);
}
public List<XmlAttribute> attributes() {
return attributes;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
if (!super.equals(o)) { return false; }
XmlElementStart that = (XmlElementStart) o;
if (attributes != null ? !attributes.equals(that.attributes) : that.attributes != null) { return false; }
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (attributes != null ? attributes.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "XmlElementStart{" +
"attributes=" + attributes +
super.toString() +
"} ";
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* XML entity reference ... {@code &#nnnn;}
*/
public class XmlEntityReference {
private final String name;
private final String text;
public XmlEntityReference(String name, String text) {
this.name = name;
this.text = text;
}
public String name() {
return name;
}
public String text() {
return text;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
XmlEntityReference that = (XmlEntityReference) o;
if (name != null ? !name.equals(that.name) : that.name != null) { return false; }
if (text != null ? !text.equals(that.text) : that.text != null) { return false; }
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (text != null ? text.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "XmlEntityReference{" +
"name='" + name + '\'' +
", text='" + text + '\'' +
'}';
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* XML namespace is part of XML element.
*/
public class XmlNamespace {
private final String prefix;
private final String uri;
public XmlNamespace(String prefix, String uri) {
this.prefix = prefix;
this.uri = uri;
}
public String prefix() {
return prefix;
}
public String uri() {
return uri;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
XmlNamespace that = (XmlNamespace) o;
if (prefix != null ? !prefix.equals(that.prefix) : that.prefix != null) { return false; }
if (uri != null ? !uri.equals(that.uri) : that.uri != null) { return false; }
return true;
}
@Override
public int hashCode() {
int result = prefix != null ? prefix.hashCode() : 0;
result = 31 * result + (uri != null ? uri.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "XmlNamespace{" +
"prefix='" + prefix + '\'' +
", uri='" + uri + '\'' +
'}';
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* XML processing instruction
*/
public class XmlProcessingInstruction {
private final String data;
private final String target;
public XmlProcessingInstruction(String data, String target) {
this.data = data;
this.target = target;
}
public String data() {
return data;
}
public String target() {
return target;
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
XmlProcessingInstruction that = (XmlProcessingInstruction) o;
if (data != null ? !data.equals(that.data) : that.data != null) { return false; }
if (target != null ? !target.equals(that.target) : that.target != null) { return false; }
return true;
}
@Override
public int hashCode() {
int result = data != null ? data.hashCode() : 0;
result = 31 * result + (target != null ? target.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "XmlProcessingInstruction{" +
"data='" + data + '\'' +
", target='" + target + '\'' +
'}';
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
/**
* White space characters
*/
public class XmlSpace extends XmlContent {
public XmlSpace(String data) {
super(data);
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* XML codec provides asynchronous and non-blocking XML parser based on the
* <a href="https://github.com/FasterXML/aalto-xml">Aalto XML parser</a>.
*/
package io.netty.handler.codec.xml;

View File

@ -0,0 +1,299 @@
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.xml;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.CharsetUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsNull.nullValue;
/**
* Verifies the basic functionality of the {@link XmlDecoder}.
* XML borrowed from <a href="http://www.studytrails.com/java/xml/woodstox/java-xml-woodstox-validation-xml-schema.jsp">
* Woodstox : Validate against XML Schema</a>
*/
public class XmlDecoderTest {
private static final String XML1 = "<?xml version=\"1.0\"?>" +
"<!DOCTYPE employee SYSTEM \"employee.dtd\">" +
"<?xml-stylesheet type=\"text/css\" href=\"netty.css\"?>" +
"<?xml-test ?>" +
"<employee xmlns:nettya=\"http://netty.io/netty/a\">" +
"<nettya:id>&plusmn;1</nettya:id>\n" +
"<name ";
private static final String XML2 = "type=\"given\">Alba</name><![CDATA[ <some data &gt;/> ]]>" +
" <!-- namespaced --><nettyb:salary xmlns:nettyb=\"http://netty.io/netty/b\" nettyb:period=\"weekly\">" +
"100</nettyb:salary><last/></employee>";
private static final String XML3 = "<?xml version=\"1.1\" encoding=\"UTf-8\" standalone=\"yes\"?><netty></netty>";
private static final String XML4 = "<netty></netty>";
private EmbeddedChannel channel;
@Before
public void setup() throws Exception {
channel = new EmbeddedChannel(new XmlDecoder());
}
@After
public void teardown() throws Exception {
channel.finish();
}
/**
* This test feeds basic XML and verifies the resulting messages
*/
@Test
public void shouldDecodeRequestWithSimpleXml() {
Object temp;
write(XML1);
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlDocumentStart.class));
assertThat(((XmlDocumentStart) temp).version(), is("1.0"));
assertThat(((XmlDocumentStart) temp).encoding(), is("UTF-8"));
assertThat(((XmlDocumentStart) temp).standalone(), is(false));
assertThat(((XmlDocumentStart) temp).encodingScheme(), is(nullValue()));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlDTD.class));
assertThat(((XmlDTD) temp).text(), is("employee.dtd"));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlProcessingInstruction.class));
assertThat(((XmlProcessingInstruction) temp).target(), is("xml-stylesheet"));
assertThat(((XmlProcessingInstruction) temp).data(), is("type=\"text/css\" href=\"netty.css\""));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlProcessingInstruction.class));
assertThat(((XmlProcessingInstruction) temp).target(), is("xml-test"));
assertThat(((XmlProcessingInstruction) temp).data(), is(""));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementStart.class));
assertThat(((XmlElementStart) temp).name(), is("employee"));
assertThat(((XmlElementStart) temp).prefix(), is(""));
assertThat(((XmlElementStart) temp).namespace(), is(""));
assertThat(((XmlElementStart) temp).attributes().size(), is(0));
assertThat(((XmlElementStart) temp).namespaces().size(), is(1));
assertThat(((XmlElementStart) temp).namespaces().get(0).prefix(), is("nettya"));
assertThat(((XmlElementStart) temp).namespaces().get(0).uri(), is("http://netty.io/netty/a"));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementStart.class));
assertThat(((XmlElementStart) temp).name(), is("id"));
assertThat(((XmlElementStart) temp).prefix(), is("nettya"));
assertThat(((XmlElementStart) temp).namespace(), is("http://netty.io/netty/a"));
assertThat(((XmlElementStart) temp).attributes().size(), is(0));
assertThat(((XmlElementStart) temp).namespaces().size(), is(0));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlEntityReference.class));
assertThat(((XmlEntityReference) temp).name(), is("plusmn"));
assertThat(((XmlEntityReference) temp).text(), is(""));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlCharacters.class));
assertThat(((XmlCharacters) temp).data(), is("1"));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementEnd.class));
assertThat(((XmlElementEnd) temp).name(), is("id"));
assertThat(((XmlElementEnd) temp).prefix(), is("nettya"));
assertThat(((XmlElementEnd) temp).namespace(), is("http://netty.io/netty/a"));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlCharacters.class));
assertThat(((XmlCharacters) temp).data(), is("\n"));
temp = channel.readInbound();
assertThat(temp, nullValue());
write(XML2);
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementStart.class));
assertThat(((XmlElementStart) temp).name(), is("name"));
assertThat(((XmlElementStart) temp).prefix(), is(""));
assertThat(((XmlElementStart) temp).namespace(), is(""));
assertThat(((XmlElementStart) temp).attributes().size(), is(1));
assertThat(((XmlElementStart) temp).attributes().get(0).name(), is("type"));
assertThat(((XmlElementStart) temp).attributes().get(0).value(), is("given"));
assertThat(((XmlElementStart) temp).attributes().get(0).prefix(), is(""));
assertThat(((XmlElementStart) temp).attributes().get(0).namespace(), is(""));
assertThat(((XmlElementStart) temp).namespaces().size(), is(0));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlCharacters.class));
assertThat(((XmlCharacters) temp).data(), is("Alba"));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementEnd.class));
assertThat(((XmlElementEnd) temp).name(), is("name"));
assertThat(((XmlElementEnd) temp).prefix(), is(""));
assertThat(((XmlElementEnd) temp).namespace(), is(""));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlCdata.class));
assertThat(((XmlCdata) temp).data(), is(" <some data &gt;/> "));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlCharacters.class));
assertThat(((XmlCharacters) temp).data(), is(" "));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlComment.class));
assertThat(((XmlComment) temp).data(), is(" namespaced "));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementStart.class));
assertThat(((XmlElementStart) temp).name(), is("salary"));
assertThat(((XmlElementStart) temp).prefix(), is("nettyb"));
assertThat(((XmlElementStart) temp).namespace(), is("http://netty.io/netty/b"));
assertThat(((XmlElementStart) temp).attributes().size(), is(1));
assertThat(((XmlElementStart) temp).attributes().get(0).name(), is("period"));
assertThat(((XmlElementStart) temp).attributes().get(0).value(), is("weekly"));
assertThat(((XmlElementStart) temp).attributes().get(0).prefix(), is("nettyb"));
assertThat(((XmlElementStart) temp).attributes().get(0).namespace(), is("http://netty.io/netty/b"));
assertThat(((XmlElementStart) temp).namespaces().size(), is(1));
assertThat(((XmlElementStart) temp).namespaces().get(0).prefix(), is("nettyb"));
assertThat(((XmlElementStart) temp).namespaces().get(0).uri(), is("http://netty.io/netty/b"));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlCharacters.class));
assertThat(((XmlCharacters) temp).data(), is("100"));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementEnd.class));
assertThat(((XmlElementEnd) temp).name(), is("salary"));
assertThat(((XmlElementEnd) temp).prefix(), is("nettyb"));
assertThat(((XmlElementEnd) temp).namespace(), is("http://netty.io/netty/b"));
assertThat(((XmlElementEnd) temp).namespaces().size(), is(1));
assertThat(((XmlElementEnd) temp).namespaces().get(0).prefix(), is("nettyb"));
assertThat(((XmlElementEnd) temp).namespaces().get(0).uri(), is("http://netty.io/netty/b"));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementStart.class));
assertThat(((XmlElementStart) temp).name(), is("last"));
assertThat(((XmlElementStart) temp).prefix(), is(""));
assertThat(((XmlElementStart) temp).namespace(), is(""));
assertThat(((XmlElementStart) temp).attributes().size(), is(0));
assertThat(((XmlElementStart) temp).namespaces().size(), is(0));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementEnd.class));
assertThat(((XmlElementEnd) temp).name(), is("last"));
assertThat(((XmlElementEnd) temp).prefix(), is(""));
assertThat(((XmlElementEnd) temp).namespace(), is(""));
assertThat(((XmlElementEnd) temp).namespaces().size(), is(0));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementEnd.class));
assertThat(((XmlElementEnd) temp).name(), is("employee"));
assertThat(((XmlElementEnd) temp).prefix(), is(""));
assertThat(((XmlElementEnd) temp).namespace(), is(""));
assertThat(((XmlElementEnd) temp).namespaces().size(), is(1));
assertThat(((XmlElementEnd) temp).namespaces().get(0).prefix(), is("nettya"));
assertThat(((XmlElementEnd) temp).namespaces().get(0).uri(), is("http://netty.io/netty/a"));
temp = channel.readInbound();
assertThat(temp, nullValue());
}
/**
* This test checks for different attributes in XML header
*/
@Test
public void shouldDecodeXmlHeader() {
Object temp;
write(XML3);
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlDocumentStart.class));
assertThat(((XmlDocumentStart) temp).version(), is("1.1"));
assertThat(((XmlDocumentStart) temp).encoding(), is("UTF-8"));
assertThat(((XmlDocumentStart) temp).standalone(), is(true));
assertThat(((XmlDocumentStart) temp).encodingScheme(), is("UTF-8"));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementStart.class));
assertThat(((XmlElementStart) temp).name(), is("netty"));
assertThat(((XmlElementStart) temp).prefix(), is(""));
assertThat(((XmlElementStart) temp).namespace(), is(""));
assertThat(((XmlElementStart) temp).attributes().size(), is(0));
assertThat(((XmlElementStart) temp).namespaces().size(), is(0));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementEnd.class));
assertThat(((XmlElementEnd) temp).name(), is("netty"));
assertThat(((XmlElementEnd) temp).prefix(), is(""));
assertThat(((XmlElementEnd) temp).namespace(), is(""));
assertThat(((XmlElementEnd) temp).namespaces().size(), is(0));
temp = channel.readInbound();
assertThat(temp, nullValue());
}
/**
* This test checks for no XML header
*/
@Test
public void shouldDecodeWithoutHeader() {
Object temp;
write(XML4);
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlDocumentStart.class));
assertThat(((XmlDocumentStart) temp).version(), is(nullValue()));
assertThat(((XmlDocumentStart) temp).encoding(), is("UTF-8"));
assertThat(((XmlDocumentStart) temp).standalone(), is(false));
assertThat(((XmlDocumentStart) temp).encodingScheme(), is(nullValue()));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementStart.class));
assertThat(((XmlElementStart) temp).name(), is("netty"));
assertThat(((XmlElementStart) temp).prefix(), is(""));
assertThat(((XmlElementStart) temp).namespace(), is(""));
assertThat(((XmlElementStart) temp).attributes().size(), is(0));
assertThat(((XmlElementStart) temp).namespaces().size(), is(0));
temp = channel.readInbound();
assertThat(temp, instanceOf(XmlElementEnd.class));
assertThat(((XmlElementEnd) temp).name(), is("netty"));
assertThat(((XmlElementEnd) temp).prefix(), is(""));
assertThat(((XmlElementEnd) temp).namespace(), is(""));
assertThat(((XmlElementEnd) temp).namespaces().size(), is(0));
temp = channel.readInbound();
assertThat(temp, nullValue());
}
private void write(String content) {
assertThat(channel.writeInbound(Unpooled.copiedBuffer(content, CharsetUtil.UTF_8)), is(true));
}
}

View File

@ -0,0 +1,13 @@
This copy of Aalto XML processor is licensed under the
Apache (Software) License, version 2.0 ("the License").
See the License for details about distribution rights, and the
specific rights regarding derivate works.
You may obtain a copy of the License at:
http://www.apache.org/licenses/
A copy is also included with both the the downloadable source code package
and jar that contains class bytecodes, as file "ASL 2.0". In both cases,
that file should be located next to this file: in source distribution
the location should be "release-notes/asl"; and in jar "META-INF/"

View File

@ -500,6 +500,7 @@
<module>codec-mqtt</module>
<module>codec-socks</module>
<module>codec-stomp</module>
<module>codec-xml</module>
<module>resolver</module>
<module>resolver-dns</module>
<module>transport</module>
@ -593,6 +594,12 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>aalto-xml</artifactId>
<version>0.9.9</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jzlib</artifactId>