--- a/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/messaging/saaj/soap/impl/DetailImpl.java Thu Jan 12 23:41:17 2017 +0000
+++ b/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/messaging/saaj/soap/impl/DetailImpl.java Fri Jan 13 01:35:15 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -47,13 +47,13 @@
public DetailEntry addDetailEntry(Name name) throws SOAPException {
DetailEntry entry = createDetailEntry(name);
addNode(entry);
- return (DetailEntry) circumventBug5034339(entry);
+ return entry;
}
public DetailEntry addDetailEntry(QName qname) throws SOAPException {
DetailEntry entry = createDetailEntry(qname);
addNode(entry);
- return (DetailEntry) circumventBug5034339(entry);
+ return entry;
}
protected SOAPElement addElement(Name name) throws SOAPException {
@@ -119,28 +119,4 @@
return true;
}
- //overriding this method since the only two uses of this method
- // are in ElementImpl and DetailImpl
- //whereas the original base impl does the correct job for calls to it inside ElementImpl
- // But it would not work for DetailImpl.
- protected SOAPElement circumventBug5034339(SOAPElement element) {
-
- Name elementName = element.getElementName();
- if (!isNamespaceQualified(elementName)) {
- String prefix = elementName.getPrefix();
- String defaultNamespace = getNamespaceURI(prefix);
- if (defaultNamespace != null) {
- Name newElementName =
- NameImpl.create(
- elementName.getLocalName(),
- elementName.getPrefix(),
- defaultNamespace);
- SOAPElement newElement = createDetailEntry(newElementName);
- replaceChild(newElement, element);
- return newElement;
- }
- }
- return element;
- }
-
}
--- a/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/messaging/saaj/soap/impl/ElementImpl.java Thu Jan 12 23:41:17 2017 +0000
+++ b/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/messaging/saaj/soap/impl/ElementImpl.java Fri Jan 13 01:35:15 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -127,8 +127,11 @@
}
public SOAPElement addChildElement(String localName) throws SOAPException {
- return (SOAPElement) addChildElement(
- NameImpl.createFromUnqualifiedName(localName));
+ String nsUri = getNamespaceURI("");
+ Name name = (nsUri == null || nsUri.isEmpty())
+ ? NameImpl.createFromUnqualifiedName(localName)
+ : NameImpl.createFromQualifiedName(localName, nsUri);
+ return addChildElement(name);
}
public SOAPElement addChildElement(String localName, String prefix)
@@ -372,13 +375,13 @@
protected SOAPElement addElement(Name name) throws SOAPException {
SOAPElement newElement = createElement(name);
addNode(newElement);
- return circumventBug5034339(newElement);
+ return newElement;
}
protected SOAPElement addElement(QName name) throws SOAPException {
SOAPElement newElement = createElement(name);
addNode(newElement);
- return circumventBug5034339(newElement);
+ return newElement;
}
protected SOAPElement createElement(Name name) {
@@ -1226,26 +1229,6 @@
return !"".equals(name.getNamespaceURI());
}
- protected SOAPElement circumventBug5034339(SOAPElement element) {
-
- Name elementName = element.getElementName();
- if (!isNamespaceQualified(elementName)) {
- String prefix = elementName.getPrefix();
- String defaultNamespace = getNamespaceURI(prefix);
- if (defaultNamespace != null) {
- Name newElementName =
- NameImpl.create(
- elementName.getLocalName(),
- elementName.getPrefix(),
- defaultNamespace);
- SOAPElement newElement = createElement(newElementName);
- replaceChild(newElement, element);
- return newElement;
- }
- }
- return element;
- }
-
//TODO: This is a temporary SAAJ workaround for optimizing XWS
// should be removed once the corresponding JAXP bug is fixed
// It appears the bug will be fixed in JAXP 1.4 (not by Appserver 9 timeframe)
--- a/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/messaging/saaj/util/stax/SaajStaxWriter.java Thu Jan 12 23:41:17 2017 +0000
+++ b/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/messaging/saaj/util/stax/SaajStaxWriter.java Fri Jan 13 01:35:15 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,10 @@
package com.sun.xml.internal.messaging.saaj.util.stax;
+import java.util.Iterator;
import java.util.Arrays;
-import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
@@ -42,6 +44,17 @@
/**
* SaajStaxWriter builds a SAAJ SOAPMessage by using XMLStreamWriter interface.
*
+ * <p>
+ * Defers creation of SOAPElement until all the aspects of the name of the element are known.
+ * In some cases, the namespace uri is indicated only by the {@link #writeNamespace(String, String)} call.
+ * After opening an element ({@code writeStartElement}, {@code writeEmptyElement} methods), all attributes
+ * and namespace assignments are retained within {@link DeferredElement} object ({@code deferredElement} field).
+ * As soon as any other method than {@code writeAttribute}, {@code writeNamespace}, {@code writeDefaultNamespace}
+ * or {@code setNamespace} is called, the contents of {@code deferredElement} is transformed into new SOAPElement
+ * (which is appropriately inserted into the SOAPMessage under construction).
+ * This mechanism is necessary to fix JDK-8159058 issue.
+ * </p>
+ *
* @author shih-chang.chen@oracle.com
*/
public class SaajStaxWriter implements XMLStreamWriter {
@@ -49,6 +62,7 @@
protected SOAPMessage soap;
protected String envURI;
protected SOAPElement currentElement;
+ protected DeferredElement deferredElement;
static final protected String Envelope = "Envelope";
static final protected String Header = "Header";
@@ -58,6 +72,7 @@
public SaajStaxWriter(final SOAPMessage msg, String uri) throws SOAPException {
soap = msg;
this.envURI = uri;
+ this.deferredElement = new DeferredElement();
}
public SOAPMessage getSOAPMessage() {
@@ -70,11 +85,8 @@
@Override
public void writeStartElement(final String localName) throws XMLStreamException {
- try {
- currentElement = currentElement.addChildElement(localName);
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
- }
+ currentElement = deferredElement.flushTo(currentElement);
+ deferredElement.setLocalName(localName);
}
@Override
@@ -84,8 +96,10 @@
@Override
public void writeStartElement(final String prefix, final String ln, final String ns) throws XMLStreamException {
- try {
- if (envURI.equals(ns)) {
+ currentElement = deferredElement.flushTo(currentElement);
+
+ if (envURI.equals(ns)) {
+ try {
if (Envelope.equals(ln)) {
currentElement = getEnvelope();
fixPrefix(prefix);
@@ -99,13 +113,16 @@
fixPrefix(prefix);
return;
}
+ } catch (SOAPException e) {
+ throw new XMLStreamException(e);
}
- currentElement = (prefix == null) ?
- currentElement.addChildElement(new QName(ns, ln)) :
- currentElement.addChildElement(ln, prefix, ns);
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
+
}
+
+ deferredElement.setLocalName(ln);
+ deferredElement.setNamespaceUri(ns);
+ deferredElement.setPrefix(prefix);
+
}
private void fixPrefix(final String prfx) throws XMLStreamException {
@@ -136,11 +153,13 @@
@Override
public void writeEndElement() throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
if (currentElement != null) currentElement = currentElement.getParentElement();
}
@Override
public void writeEndDocument() throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
}
@Override
@@ -158,19 +177,14 @@
@Override
public void writeAttribute(final String prefix, final String ns, final String ln, final String value) throws XMLStreamException {
- try {
- if (ns == null) {
- if (prefix == null && xmlns.equals(ln)) {
- currentElement.addNamespaceDeclaration("", value);
- } else {
- currentElement.setAttributeNS("", ln, value);
- }
+ if (ns == null && prefix == null && xmlns.equals(ln)) {
+ writeNamespace("", value);
+ } else {
+ if (deferredElement.isInitialized()) {
+ deferredElement.addAttribute(prefix, ns, ln, value);
} else {
- QName name = (prefix == null) ? new QName(ns, ln) : new QName(ns, ln, prefix);
- currentElement.addAttribute(name, value);
+ addAttibuteToElement(currentElement, prefix, ns, ln, value);
}
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
}
}
@@ -181,16 +195,16 @@
@Override
public void writeNamespace(String prefix, final String uri) throws XMLStreamException {
-
// make prefix default if null or "xmlns" (according to javadoc)
- if (prefix == null || "xmlns".equals(prefix)) {
- prefix = "";
- }
-
- try {
- currentElement.addNamespaceDeclaration(prefix, uri);
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
+ String thePrefix = prefix == null || "xmlns".equals(prefix) ? "" : prefix;
+ if (deferredElement.isInitialized()) {
+ deferredElement.addNamespaceDeclaration(thePrefix, uri);
+ } else {
+ try {
+ currentElement.addNamespaceDeclaration(thePrefix, uri);
+ } catch (SOAPException e) {
+ throw new XMLStreamException(e);
+ }
}
}
@@ -201,35 +215,40 @@
@Override
public void writeComment(final String data) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Comment c = soap.getSOAPPart().createComment(data);
currentElement.appendChild(c);
}
@Override
public void writeProcessingInstruction(final String target) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Node n = soap.getSOAPPart().createProcessingInstruction(target, "");
currentElement.appendChild(n);
}
@Override
public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Node n = soap.getSOAPPart().createProcessingInstruction(target, data);
currentElement.appendChild(n);
}
@Override
public void writeCData(final String data) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Node n = soap.getSOAPPart().createCDATASection(data);
currentElement.appendChild(n);
}
@Override
public void writeDTD(final String dtd) throws XMLStreamException {
- //TODO ... Don't do anything here
+ currentElement = deferredElement.flushTo(currentElement);
}
@Override
public void writeEntityRef(final String name) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Node n = soap.getSOAPPart().createEntityReference(name);
currentElement.appendChild(n);
}
@@ -257,6 +276,7 @@
@Override
public void writeCharacters(final String text) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
try {
currentElement.addTextNode(text);
} catch (SOAPException e) {
@@ -266,6 +286,7 @@
@Override
public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
char[] chr = (start == 0 && len == text.length) ? text : Arrays.copyOfRange(text, start, start + len);
try {
currentElement.addTextNode(new String(chr));
@@ -281,10 +302,16 @@
@Override
public void setPrefix(final String prefix, final String uri) throws XMLStreamException {
- try {
- this.currentElement.addNamespaceDeclaration(prefix, uri);
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
+ // TODO: this in fact is not what would be expected from XMLStreamWriter
+ // (e.g. XMLStreamWriter for writing to output stream does not write anything as result of
+ // this method, it just rememebers that given prefix is associated with the given uri
+ // for the scope; to actually declare the prefix assignment in the resulting XML, one
+ // needs to call writeNamespace(...) method
+ // Kept for backwards compatibility reasons - this might be worth of further investigation.
+ if (deferredElement.isInitialized()) {
+ deferredElement.addNamespaceDeclaration(prefix, uri);
+ } else {
+ throw new XMLStreamException("Namespace not associated with any element");
}
}
@@ -331,4 +358,209 @@
}
};
}
+
+ static void addAttibuteToElement(SOAPElement element, String prefix, String ns, String ln, String value)
+ throws XMLStreamException {
+ try {
+ if (ns == null) {
+ element.setAttributeNS("", ln, value);
+ } else {
+ QName name = prefix == null ? new QName(ns, ln) : new QName(ns, ln, prefix);
+ element.addAttribute(name, value);
+ }
+ } catch (SOAPException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ /**
+ * Holds details of element that needs to be deferred in order to manage namespace assignments correctly.
+ *
+ * <p>
+ * An instance of can be set with all the aspects of the element name (local name, prefix, namespace uri).
+ * Attributes and namespace declarations (special case of attribute) can be added.
+ * Namespace declarations are handled so that the element namespace is updated if it is implied by the namespace
+ * declaration and the namespace was not set to non-{@code null} value previously.
+ * </p>
+ *
+ * <p>
+ * The state of this object can be {@link #flushTo(SOAPElement) flushed} to SOAPElement - new SOAPElement will
+ * be added a child element; the new element will have exactly the shape as represented by the state of this
+ * object. Note that the {@link #flushTo(SOAPElement)} method does nothing
+ * (and returns the argument immediately) if the state of this object is not initialized
+ * (i.e. local name is null).
+ * </p>
+ *
+ * @author ondrej.cerny@oracle.com
+ */
+ static class DeferredElement {
+ private String prefix;
+ private String localName;
+ private String namespaceUri;
+ private final List<NamespaceDeclaration> namespaceDeclarations;
+ private final List<AttributeDeclaration> attributeDeclarations;
+
+ DeferredElement() {
+ this.namespaceDeclarations = new LinkedList<NamespaceDeclaration>();
+ this.attributeDeclarations = new LinkedList<AttributeDeclaration>();
+ reset();
+ }
+
+
+ /**
+ * Set prefix of the element.
+ * @param prefix namespace prefix
+ */
+ public void setPrefix(final String prefix) {
+ this.prefix = prefix;
+ }
+
+ /**
+ * Set local name of the element.
+ *
+ * <p>
+ * This method initializes the element.
+ * </p>
+ *
+ * @param localName local name {@code not null}
+ */
+ public void setLocalName(final String localName) {
+ if (localName == null) {
+ throw new IllegalArgumentException("localName can not be null");
+ }
+ this.localName = localName;
+ }
+
+ /**
+ * Set namespace uri.
+ *
+ * @param namespaceUri namespace uri
+ */
+ public void setNamespaceUri(final String namespaceUri) {
+ this.namespaceUri = namespaceUri;
+ }
+
+ /**
+ * Adds namespace prefix assignment to the element.
+ *
+ * @param prefix prefix (not {@code null})
+ * @param namespaceUri namespace uri
+ */
+ public void addNamespaceDeclaration(final String prefix, final String namespaceUri) {
+ if (null == this.namespaceUri && null != namespaceUri && prefix.equals(emptyIfNull(this.prefix))) {
+ this.namespaceUri = namespaceUri;
+ }
+ this.namespaceDeclarations.add(new NamespaceDeclaration(prefix, namespaceUri));
+ }
+
+ /**
+ * Adds attribute to the element.
+ * @param prefix prefix
+ * @param ns namespace
+ * @param ln local name
+ * @param value value
+ */
+ public void addAttribute(final String prefix, final String ns, final String ln, final String value) {
+ if (ns == null && prefix == null && xmlns.equals(ln)) {
+ this.addNamespaceDeclaration(prefix, value);
+ } else {
+ this.attributeDeclarations.add(new AttributeDeclaration(prefix, ns, ln, value));
+ }
+ }
+
+ /**
+ * Flushes state of this element to the {@code target} element.
+ *
+ * <p>
+ * If this element is initialized then it is added with all the namespace declarations and attributes
+ * to the {@code target} element as a child. The state of this element is reset to uninitialized.
+ * The newly added element object is returned.
+ * </p>
+ * <p>
+ * If this element is not initialized then the {@code target} is returned immediately, nothing else is done.
+ * </p>
+ *
+ * @param target target element
+ * @return {@code target} or new element
+ * @throws XMLStreamException on error
+ */
+ public SOAPElement flushTo(final SOAPElement target) throws XMLStreamException {
+ try {
+ if (this.localName != null) {
+ // add the element appropriately (based on namespace declaration)
+ final SOAPElement newElement;
+ if (this.namespaceUri == null) {
+ // add element with inherited scope
+ newElement = target.addChildElement(this.localName);
+ } else if (prefix == null) {
+ newElement = target.addChildElement(new QName(this.namespaceUri, this.localName));
+ } else {
+ newElement = target.addChildElement(this.localName, this.prefix, this.namespaceUri);
+ }
+ // add namespace declarations
+ for (NamespaceDeclaration namespace : this.namespaceDeclarations) {
+ target.addNamespaceDeclaration(namespace.prefix, namespace.namespaceUri);
+ }
+ // add attribute declarations
+ for (AttributeDeclaration attribute : this.attributeDeclarations) {
+ addAttibuteToElement(newElement,
+ attribute.prefix, attribute.namespaceUri, attribute.localName, attribute.value);
+ }
+ // reset state
+ this.reset();
+
+ return newElement;
+ } else {
+ return target;
+ }
+ // else after reset state -> not initialized
+ } catch (SOAPException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ /**
+ * Is the element initialized?
+ * @return boolean indicating whether it was initialized after last flush
+ */
+ public boolean isInitialized() {
+ return this.localName != null;
+ }
+
+ private void reset() {
+ this.localName = null;
+ this.prefix = null;
+ this.namespaceUri = null;
+ this.namespaceDeclarations.clear();
+ this.attributeDeclarations.clear();
+ }
+
+ private static String emptyIfNull(String s) {
+ return s == null ? "" : s;
+ }
+ }
+
+ static class NamespaceDeclaration {
+ final String prefix;
+ final String namespaceUri;
+
+ NamespaceDeclaration(String prefix, String namespaceUri) {
+ this.prefix = prefix;
+ this.namespaceUri = namespaceUri;
+ }
+ }
+
+ static class AttributeDeclaration {
+ final String prefix;
+ final String namespaceUri;
+ final String localName;
+ final String value;
+
+ AttributeDeclaration(String prefix, String namespaceUri, String localName, String value) {
+ this.prefix = prefix;
+ this.namespaceUri = namespaceUri;
+ this.localName = localName;
+ this.value = value;
+ }
+ }
}
--- a/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/ws/api/message/saaj/SaajStaxWriter.java Thu Jan 12 23:41:17 2017 +0000
+++ b/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/ws/api/message/saaj/SaajStaxWriter.java Fri Jan 13 01:35:15 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,8 +25,10 @@
package com.sun.xml.internal.ws.api.message.saaj;
+import java.util.Iterator;
import java.util.Arrays;
-import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
@@ -42,6 +44,17 @@
/**
* SaajStaxWriter builds a SAAJ SOAPMessage by using XMLStreamWriter interface.
*
+ * <p>
+ * Defers creation of SOAPElement until all the aspects of the name of the element are known.
+ * In some cases, the namespace uri is indicated only by the {@link #writeNamespace(String, String)} call.
+ * After opening an element ({@code writeStartElement}, {@code writeEmptyElement} methods), all attributes
+ * and namespace assignments are retained within {@link DeferredElement} object ({@code deferredElement} field).
+ * As soon as any other method than {@code writeAttribute}, {@code writeNamespace}, {@code writeDefaultNamespace}
+ * or {@code setNamespace} is called, the contents of {@code deferredElement} is transformed into new SOAPElement
+ * (which is appropriately inserted into the SOAPMessage under construction).
+ * This mechanism is necessary to fix JDK-8159058 issue.
+ * </p>
+ *
* @author shih-chang.chen@oracle.com
*/
public class SaajStaxWriter implements XMLStreamWriter {
@@ -49,6 +62,7 @@
protected SOAPMessage soap;
protected String envURI;
protected SOAPElement currentElement;
+ protected DeferredElement deferredElement;
static final protected String Envelope = "Envelope";
static final protected String Header = "Header";
@@ -58,6 +72,7 @@
public SaajStaxWriter(final SOAPMessage msg, String uri) throws SOAPException {
soap = msg;
this.envURI = uri;
+ this.deferredElement = new DeferredElement();
}
public SOAPMessage getSOAPMessage() {
@@ -70,11 +85,8 @@
@Override
public void writeStartElement(final String localName) throws XMLStreamException {
- try {
- currentElement = currentElement.addChildElement(localName);
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
- }
+ currentElement = deferredElement.flushTo(currentElement);
+ deferredElement.setLocalName(localName);
}
@Override
@@ -84,8 +96,10 @@
@Override
public void writeStartElement(final String prefix, final String ln, final String ns) throws XMLStreamException {
- try {
- if (envURI.equals(ns)) {
+ currentElement = deferredElement.flushTo(currentElement);
+
+ if (envURI.equals(ns)) {
+ try {
if (Envelope.equals(ln)) {
currentElement = getEnvelope();
fixPrefix(prefix);
@@ -99,13 +113,16 @@
fixPrefix(prefix);
return;
}
+ } catch (SOAPException e) {
+ throw new XMLStreamException(e);
}
- currentElement = (prefix == null) ?
- currentElement.addChildElement(new QName(ns, ln)) :
- currentElement.addChildElement(ln, prefix, ns);
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
+
}
+
+ deferredElement.setLocalName(ln);
+ deferredElement.setNamespaceUri(ns);
+ deferredElement.setPrefix(prefix);
+
}
private void fixPrefix(final String prfx) throws XMLStreamException {
@@ -136,11 +153,13 @@
@Override
public void writeEndElement() throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
if (currentElement != null) currentElement = currentElement.getParentElement();
}
@Override
public void writeEndDocument() throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
}
@Override
@@ -158,19 +177,14 @@
@Override
public void writeAttribute(final String prefix, final String ns, final String ln, final String value) throws XMLStreamException {
- try {
- if (ns == null) {
- if (prefix == null && xmlns.equals(ln)) {
- currentElement.addNamespaceDeclaration("", value);
- } else {
- currentElement.setAttributeNS("", ln, value);
- }
+ if (ns == null && prefix == null && xmlns.equals(ln)) {
+ writeNamespace("", value);
+ } else {
+ if (deferredElement.isInitialized()) {
+ deferredElement.addAttribute(prefix, ns, ln, value);
} else {
- QName name = (prefix == null) ? new QName(ns, ln) : new QName(ns, ln, prefix);
- currentElement.addAttribute(name, value);
+ addAttibuteToElement(currentElement, prefix, ns, ln, value);
}
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
}
}
@@ -181,16 +195,16 @@
@Override
public void writeNamespace(String prefix, final String uri) throws XMLStreamException {
-
// make prefix default if null or "xmlns" (according to javadoc)
- if (prefix == null || "xmlns".equals(prefix)) {
- prefix = "";
- }
-
- try {
- currentElement.addNamespaceDeclaration(prefix, uri);
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
+ String thePrefix = prefix == null || "xmlns".equals(prefix) ? "" : prefix;
+ if (deferredElement.isInitialized()) {
+ deferredElement.addNamespaceDeclaration(thePrefix, uri);
+ } else {
+ try {
+ currentElement.addNamespaceDeclaration(thePrefix, uri);
+ } catch (SOAPException e) {
+ throw new XMLStreamException(e);
+ }
}
}
@@ -201,35 +215,40 @@
@Override
public void writeComment(final String data) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Comment c = soap.getSOAPPart().createComment(data);
currentElement.appendChild(c);
}
@Override
public void writeProcessingInstruction(final String target) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Node n = soap.getSOAPPart().createProcessingInstruction(target, "");
currentElement.appendChild(n);
}
@Override
public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Node n = soap.getSOAPPart().createProcessingInstruction(target, data);
currentElement.appendChild(n);
}
@Override
public void writeCData(final String data) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Node n = soap.getSOAPPart().createCDATASection(data);
currentElement.appendChild(n);
}
@Override
public void writeDTD(final String dtd) throws XMLStreamException {
- //TODO ... Don't do anything here
+ currentElement = deferredElement.flushTo(currentElement);
}
@Override
public void writeEntityRef(final String name) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
Node n = soap.getSOAPPart().createEntityReference(name);
currentElement.appendChild(n);
}
@@ -257,6 +276,7 @@
@Override
public void writeCharacters(final String text) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
try {
currentElement.addTextNode(text);
} catch (SOAPException e) {
@@ -266,6 +286,7 @@
@Override
public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException {
+ currentElement = deferredElement.flushTo(currentElement);
char[] chr = (start == 0 && len == text.length) ? text : Arrays.copyOfRange(text, start, start + len);
try {
currentElement.addTextNode(new String(chr));
@@ -281,10 +302,16 @@
@Override
public void setPrefix(final String prefix, final String uri) throws XMLStreamException {
- try {
- this.currentElement.addNamespaceDeclaration(prefix, uri);
- } catch (SOAPException e) {
- throw new XMLStreamException(e);
+ // TODO: this in fact is not what would be expected from XMLStreamWriter
+ // (e.g. XMLStreamWriter for writing to output stream does not write anything as result of
+ // this method, it just rememebers that given prefix is associated with the given uri
+ // for the scope; to actually declare the prefix assignment in the resulting XML, one
+ // needs to call writeNamespace(...) method
+ // Kept for backwards compatibility reasons - this might be worth of further investigation.
+ if (deferredElement.isInitialized()) {
+ deferredElement.addNamespaceDeclaration(prefix, uri);
+ } else {
+ throw new XMLStreamException("Namespace not associated with any element");
}
}
@@ -315,12 +342,12 @@
return currentElement.lookupPrefix(namespaceURI);
}
public Iterator getPrefixes(final String namespaceURI) {
- return new Iterator() {
+ return new Iterator<String>() {
String prefix = getPrefix(namespaceURI);
public boolean hasNext() {
return (prefix != null);
}
- public Object next() {
+ public String next() {
if (!hasNext()) throw new java.util.NoSuchElementException();
String next = prefix;
prefix = null;
@@ -331,4 +358,209 @@
}
};
}
+
+ static void addAttibuteToElement(SOAPElement element, String prefix, String ns, String ln, String value)
+ throws XMLStreamException {
+ try {
+ if (ns == null) {
+ element.setAttributeNS("", ln, value);
+ } else {
+ QName name = prefix == null ? new QName(ns, ln) : new QName(ns, ln, prefix);
+ element.addAttribute(name, value);
+ }
+ } catch (SOAPException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ /**
+ * Holds details of element that needs to be deferred in order to manage namespace assignments correctly.
+ *
+ * <p>
+ * An instance of can be set with all the aspects of the element name (local name, prefix, namespace uri).
+ * Attributes and namespace declarations (special case of attribute) can be added.
+ * Namespace declarations are handled so that the element namespace is updated if it is implied by the namespace
+ * declaration and the namespace was not set to non-{@code null} value previously.
+ * </p>
+ *
+ * <p>
+ * The state of this object can be {@link #flushTo(SOAPElement) flushed} to SOAPElement - new SOAPElement will
+ * be added a child element; the new element will have exactly the shape as represented by the state of this
+ * object. Note that the {@link #flushTo(SOAPElement)} method does nothing
+ * (and returns the argument immediately) if the state of this object is not initialized
+ * (i.e. local name is null).
+ * </p>
+ *
+ * @author ondrej.cerny@oracle.com
+ */
+ static class DeferredElement {
+ private String prefix;
+ private String localName;
+ private String namespaceUri;
+ private final List<NamespaceDeclaration> namespaceDeclarations;
+ private final List<AttributeDeclaration> attributeDeclarations;
+
+ DeferredElement() {
+ this.namespaceDeclarations = new LinkedList<NamespaceDeclaration>();
+ this.attributeDeclarations = new LinkedList<AttributeDeclaration>();
+ reset();
+ }
+
+
+ /**
+ * Set prefix of the element.
+ * @param prefix namespace prefix
+ */
+ public void setPrefix(final String prefix) {
+ this.prefix = prefix;
+ }
+
+ /**
+ * Set local name of the element.
+ *
+ * <p>
+ * This method initializes the element.
+ * </p>
+ *
+ * @param localName local name {@code not null}
+ */
+ public void setLocalName(final String localName) {
+ if (localName == null) {
+ throw new IllegalArgumentException("localName can not be null");
+ }
+ this.localName = localName;
+ }
+
+ /**
+ * Set namespace uri.
+ *
+ * @param namespaceUri namespace uri
+ */
+ public void setNamespaceUri(final String namespaceUri) {
+ this.namespaceUri = namespaceUri;
+ }
+
+ /**
+ * Adds namespace prefix assignment to the element.
+ *
+ * @param prefix prefix (not {@code null})
+ * @param namespaceUri namespace uri
+ */
+ public void addNamespaceDeclaration(final String prefix, final String namespaceUri) {
+ if (null == this.namespaceUri && null != namespaceUri && prefix.equals(emptyIfNull(this.prefix))) {
+ this.namespaceUri = namespaceUri;
+ }
+ this.namespaceDeclarations.add(new NamespaceDeclaration(prefix, namespaceUri));
+ }
+
+ /**
+ * Adds attribute to the element.
+ * @param prefix prefix
+ * @param ns namespace
+ * @param ln local name
+ * @param value value
+ */
+ public void addAttribute(final String prefix, final String ns, final String ln, final String value) {
+ if (ns == null && prefix == null && xmlns.equals(ln)) {
+ this.addNamespaceDeclaration(prefix, value);
+ } else {
+ this.attributeDeclarations.add(new AttributeDeclaration(prefix, ns, ln, value));
+ }
+ }
+
+ /**
+ * Flushes state of this element to the {@code target} element.
+ *
+ * <p>
+ * If this element is initialized then it is added with all the namespace declarations and attributes
+ * to the {@code target} element as a child. The state of this element is reset to uninitialized.
+ * The newly added element object is returned.
+ * </p>
+ * <p>
+ * If this element is not initialized then the {@code target} is returned immediately, nothing else is done.
+ * </p>
+ *
+ * @param target target element
+ * @return {@code target} or new element
+ * @throws XMLStreamException on error
+ */
+ public SOAPElement flushTo(final SOAPElement target) throws XMLStreamException {
+ try {
+ if (this.localName != null) {
+ // add the element appropriately (based on namespace declaration)
+ final SOAPElement newElement;
+ if (this.namespaceUri == null) {
+ // add element with inherited scope
+ newElement = target.addChildElement(this.localName);
+ } else if (prefix == null) {
+ newElement = target.addChildElement(new QName(this.namespaceUri, this.localName));
+ } else {
+ newElement = target.addChildElement(this.localName, this.prefix, this.namespaceUri);
+ }
+ // add namespace declarations
+ for (NamespaceDeclaration namespace : this.namespaceDeclarations) {
+ target.addNamespaceDeclaration(namespace.prefix, namespace.namespaceUri);
+ }
+ // add attribute declarations
+ for (AttributeDeclaration attribute : this.attributeDeclarations) {
+ addAttibuteToElement(newElement,
+ attribute.prefix, attribute.namespaceUri, attribute.localName, attribute.value);
+ }
+ // reset state
+ this.reset();
+
+ return newElement;
+ } else {
+ return target;
+ }
+ // else after reset state -> not initialized
+ } catch (SOAPException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ /**
+ * Is the element initialized?
+ * @return boolean indicating whether it was initialized after last flush
+ */
+ public boolean isInitialized() {
+ return this.localName != null;
+ }
+
+ private void reset() {
+ this.localName = null;
+ this.prefix = null;
+ this.namespaceUri = null;
+ this.namespaceDeclarations.clear();
+ this.attributeDeclarations.clear();
+ }
+
+ private static String emptyIfNull(String s) {
+ return s == null ? "" : s;
+ }
+ }
+
+ static class NamespaceDeclaration {
+ final String prefix;
+ final String namespaceUri;
+
+ NamespaceDeclaration(String prefix, String namespaceUri) {
+ this.prefix = prefix;
+ this.namespaceUri = namespaceUri;
+ }
+ }
+
+ static class AttributeDeclaration {
+ final String prefix;
+ final String namespaceUri;
+ final String localName;
+ final String value;
+
+ AttributeDeclaration(String prefix, String namespaceUri, String localName, String value) {
+ this.prefix = prefix;
+ this.namespaceUri = namespaceUri;
+ this.localName = localName;
+ this.value = value;
+ }
+ }
}
--- a/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/ws/util/xml/XmlUtil.java Thu Jan 12 23:41:17 2017 +0000
+++ b/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/ws/util/xml/XmlUtil.java Fri Jan 13 01:35:15 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,6 +33,7 @@
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Method;
+import java.net.URI;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -332,13 +333,13 @@
* (com.sun.org.apache.xml.internal) for modular runtime.
*/
private static EntityResolver createCatalogResolver(ArrayList<URL> urls) throws Exception {
- // Prepare array of catalog paths
- String[] paths = urls.stream()
- .map(u -> u.toExternalForm())
- .toArray(c -> new String[c]);
+ // Prepare array of catalog URIs
+ URI[] uris = urls.stream()
+ .map(u -> URI.create(u.toExternalForm()))
+ .toArray(URI[]::new);
//Create CatalogResolver with new JDK9+ API
- return (EntityResolver) CatalogManager.catalogResolver(catalogFeatures, paths);
+ return (EntityResolver) CatalogManager.catalogResolver(catalogFeatures, uris);
}
// Cache CatalogFeatures instance for future usages.
--- a/jaxws/src/jdk.xml.bind/share/classes/com/sun/tools/internal/xjc/Options.java Thu Jan 12 23:41:17 2017 +0000
+++ b/jaxws/src/jdk.xml.bind/share/classes/com/sun/tools/internal/xjc/Options.java Fri Jan 13 01:35:15 2017 +0000
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -777,13 +777,13 @@
* Adds a new catalog file.
*/
public void addCatalog(File catalogFile) throws IOException {
- String newUrl = catalogFile.getPath();
+ URI newUrl = catalogFile.toURI();
if (!catalogUrls.contains(newUrl)) {
catalogUrls.add(newUrl);
}
try {
entityResolver = CatalogManager.catalogResolver(catalogFeatures,
- catalogUrls.toArray(new String[0]));
+ catalogUrls.stream().toArray(URI[]::new));
} catch (Exception ex) {
entityResolver = null;
}
@@ -791,7 +791,7 @@
// Since javax.xml.catalog is unmodifiable we need to track catalog
// URLs added and create new catalog each time addCatalog is called
- private final ArrayList<String> catalogUrls = new ArrayList<String>();
+ private final ArrayList<URI> catalogUrls = new ArrayList<>();
// Cache CatalogFeatures instance for future usages.
// Resolve feature is set to "continue" value for backward compatibility.