Fri, 04 Oct 2013 16:21:34 +0100
8025054: Update JAX-WS RI integration to 2.2.9-b130926.1035
Reviewed-by: chegar
1 /*
2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.tools.internal.ws.wsdl.parser;
28 import com.sun.istack.internal.NotNull;
29 import com.sun.istack.internal.Nullable;
30 import com.sun.istack.internal.SAXParseException2;
31 import com.sun.tools.internal.ws.resources.WsdlMessages;
32 import com.sun.tools.internal.ws.wscompile.ErrorReceiver;
33 import com.sun.tools.internal.ws.wscompile.WsimportOptions;
34 import com.sun.tools.internal.ws.wsdl.document.jaxws.JAXWSBindingsConstants;
35 import com.sun.tools.internal.xjc.util.DOMUtils;
36 import com.sun.xml.internal.bind.v2.util.EditDistance;
37 import com.sun.xml.internal.ws.util.DOMUtil;
38 import com.sun.xml.internal.ws.util.JAXWSUtils;
39 import com.sun.xml.internal.ws.util.xml.XmlUtil;
40 import org.w3c.dom.*;
41 import org.xml.sax.SAXParseException;
43 import javax.xml.namespace.NamespaceContext;
44 import javax.xml.xpath.XPath;
45 import javax.xml.xpath.XPathConstants;
46 import javax.xml.xpath.XPathExpressionException;
47 import javax.xml.xpath.XPathFactory;
48 import java.net.MalformedURLException;
49 import java.net.URL;
50 import java.util.ArrayList;
51 import java.util.HashSet;
52 import java.util.Iterator;
53 import java.util.Set;
56 /**
57 * Internalizes external binding declarations.
58 *
59 * @author Vivek Pandey
60 */
61 public class Internalizer {
63 private static final XPathFactory xpf = XmlUtil.newXPathFactory(true);
64 private final XPath xpath = xpf.newXPath();
65 private final DOMForest forest;
66 private final ErrorReceiver errorReceiver;
69 public Internalizer(DOMForest forest, WsimportOptions options, ErrorReceiver errorReceiver) {
70 this.forest = forest;
71 this.errorReceiver = errorReceiver;
72 }
74 public void transform() {
75 for (Element jaxwsBinding : forest.outerMostBindings) {
76 internalize(jaxwsBinding, jaxwsBinding);
77 }
78 }
80 /**
81 * Validates attributes of a <JAXWS:bindings> element.
82 */
83 private void validate(Element bindings) {
84 NamedNodeMap atts = bindings.getAttributes();
85 for (int i = 0; i < atts.getLength(); i++) {
86 Attr a = (Attr) atts.item(i);
87 if (a.getNamespaceURI() != null) {
88 continue; // all foreign namespace OK.
89 }
90 if (a.getLocalName().equals("node")) {
91 continue;
92 }
93 if (a.getLocalName().equals("wsdlLocation")) {
94 continue;
95 }
97 // TODO: flag error for this undefined attribute
98 }
99 }
101 private void internalize(Element bindings, Node inheritedTarget) {
102 // start by the inherited target
103 Node target = inheritedTarget;
105 validate(bindings); // validate this node
107 // look for @wsdlLocation
108 if (isTopLevelBinding(bindings)) {
109 String wsdlLocation;
110 if (bindings.getAttributeNode("wsdlLocation") != null) {
111 wsdlLocation = bindings.getAttribute("wsdlLocation");
113 try {
114 // absolutize this URI.
115 // TODO: use the URI class
116 // TODO: honor xml:base
117 wsdlLocation = new URL(new URL(forest.getSystemId(bindings.getOwnerDocument())),
118 wsdlLocation).toExternalForm();
119 } catch (MalformedURLException e) {
120 wsdlLocation = JAXWSUtils.absolutize(JAXWSUtils.getFileOrURLName(wsdlLocation));
121 }
122 } else {
123 //the node does not have
124 wsdlLocation = forest.getFirstRootDocument();
125 }
126 target = forest.get(wsdlLocation);
128 if (target == null) {
129 reportError(bindings, WsdlMessages.INTERNALIZER_INCORRECT_SCHEMA_REFERENCE(wsdlLocation, EditDistance.findNearest(wsdlLocation, forest.listSystemIDs())));
130 return; // abort processing this <JAXWS:bindings>
131 }
132 }
134 //if the target node is xs:schema, declare the jaxb version on it as latter on it will be
135 //required by the inlined schema bindings
137 Element element = DOMUtil.getFirstElementChild(target);
138 if (element != null && element.getNamespaceURI().equals(Constants.NS_WSDL) && element.getLocalName().equals("definitions")) {
139 //get all schema elements
140 Element type = DOMUtils.getFirstChildElement(element, Constants.NS_WSDL, "types");
141 if (type != null) {
142 for (Element schemaElement : DOMUtils.getChildElements(type, Constants.NS_XSD, "schema")) {
143 if (!schemaElement.hasAttributeNS(Constants.NS_XMLNS, "jaxb")) {
144 schemaElement.setAttributeNS(Constants.NS_XMLNS, "xmlns:jaxb", JAXWSBindingsConstants.NS_JAXB_BINDINGS);
145 }
147 //add jaxb:bindings version info. Lets put it to 1.0, may need to change latter
148 if (!schemaElement.hasAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "version")) {
149 schemaElement.setAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "jaxb:version", JAXWSBindingsConstants.JAXB_BINDING_VERSION);
150 }
151 }
152 }
153 }
156 NodeList targetNodes = null;
157 boolean hasNode = true;
158 boolean isToplevelBinding = isTopLevelBinding(bindings);
159 if ((isJAXWSBindings(bindings) || isJAXBBindings(bindings)) && bindings.getAttributeNode("node") != null) {
160 targetNodes = evaluateXPathMultiNode(bindings, target, bindings.getAttribute("node"), new NamespaceContextImpl(bindings));
161 } else
162 if (isJAXWSBindings(bindings) && (bindings.getAttributeNode("node") == null) && !isToplevelBinding) {
163 hasNode = false;
164 } else
165 if (isGlobalBinding(bindings) && !isWSDLDefinition(target) && isTopLevelBinding(bindings.getParentNode())) {
166 targetNodes = getWSDLDefintionNode(bindings, target);
167 }
169 //if target is null or empty it means the xpath evaluation has some problem,
170 // just return
171 if (targetNodes == null && hasNode && !isToplevelBinding) {
172 return;
173 }
175 if (hasNode) {
176 if (targetNodes != null) {
177 for (int i = 0; i < targetNodes.getLength(); i++) {
178 insertBinding(bindings, targetNodes.item(i));
179 // look for child <JAXWS:bindings> and process them recursively
180 Element[] children = getChildElements(bindings);
181 for (Element child : children) {
182 if ("bindings".equals(child.getLocalName())) {
183 internalize(child, targetNodes.item(i));
184 }
185 }
186 }
187 }
188 }
189 if (targetNodes == null) {
190 // look for child <JAXWS:bindings> and process them recursively
191 Element[] children = getChildElements(bindings);
193 for (Element child : children) {
194 internalize(child, target);
195 }
196 }
197 }
199 /**
200 * Moves JAXWS customizations under their respective target nodes.
201 */
202 private void insertBinding(@NotNull Element bindings, @NotNull Node target) {
203 if ("bindings".equals(bindings.getLocalName())) {
204 Element[] children = DOMUtils.getChildElements(bindings);
205 for (Element item : children) {
206 if ("bindings".equals(item.getLocalName())) {
207 //done
208 } else {
209 moveUnder(item, (Element) target);
211 }
212 }
213 } else {
214 moveUnder(bindings, (Element) target);
215 }
216 }
218 private NodeList getWSDLDefintionNode(Node bindings, Node target) {
219 return evaluateXPathMultiNode(bindings, target, "wsdl:definitions",
220 new NamespaceContext() {
221 @Override
222 public String getNamespaceURI(String prefix) {
223 return "http://schemas.xmlsoap.org/wsdl/";
224 }
226 @Override
227 public String getPrefix(String nsURI) {
228 throw new UnsupportedOperationException();
229 }
231 @Override
232 public Iterator getPrefixes(String namespaceURI) {
233 throw new UnsupportedOperationException();
234 }
235 });
236 }
238 private boolean isWSDLDefinition(Node target) {
239 if (target == null) {
240 return false;
241 }
242 String localName = target.getLocalName();
243 String nsURI = target.getNamespaceURI();
244 return fixNull(localName).equals("definitions") && fixNull(nsURI).equals("http://schemas.xmlsoap.org/wsdl/");
245 }
247 private boolean isTopLevelBinding(Node node) {
248 return node.getOwnerDocument().getDocumentElement() == node;
249 }
251 private boolean isJAXWSBindings(Node bindings) {
252 return (bindings.getNamespaceURI().equals(JAXWSBindingsConstants.NS_JAXWS_BINDINGS) && bindings.getLocalName().equals("bindings"));
253 }
255 private boolean isJAXBBindings(Node bindings) {
256 return (bindings.getNamespaceURI().equals(JAXWSBindingsConstants.NS_JAXB_BINDINGS) && bindings.getLocalName().equals("bindings"));
257 }
259 private boolean isGlobalBinding(Node bindings) {
260 if (bindings.getNamespaceURI() == null) {
261 errorReceiver.warning(forest.locatorTable.getStartLocation((Element) bindings), WsdlMessages.INVALID_CUSTOMIZATION_NAMESPACE(bindings.getLocalName()));
262 return false;
263 }
264 return (bindings.getNamespaceURI().equals(JAXWSBindingsConstants.NS_JAXWS_BINDINGS) &&
265 (bindings.getLocalName().equals("package") ||
266 bindings.getLocalName().equals("enableAsyncMapping") ||
267 bindings.getLocalName().equals("enableAdditionalSOAPHeaderMapping") ||
268 bindings.getLocalName().equals("enableWrapperStyle") ||
269 bindings.getLocalName().equals("enableMIMEContent")));
270 }
272 private static Element[] getChildElements(Element parent) {
273 ArrayList<Element> a = new ArrayList<Element>();
274 NodeList children = parent.getChildNodes();
275 for (int i = 0; i < children.getLength(); i++) {
276 Node item = children.item(i);
277 if (!(item instanceof Element)) {
278 continue;
279 }
280 if (JAXWSBindingsConstants.NS_JAXWS_BINDINGS.equals(item.getNamespaceURI()) ||
281 JAXWSBindingsConstants.NS_JAXB_BINDINGS.equals(item.getNamespaceURI())) {
282 a.add((Element) item);
283 }
284 }
285 return a.toArray(new Element[a.size()]);
286 }
288 private NodeList evaluateXPathMultiNode(Node bindings, Node target, String expression, NamespaceContext namespaceContext) {
289 NodeList nlst;
290 try {
291 xpath.setNamespaceContext(namespaceContext);
292 nlst = (NodeList) xpath.evaluate(expression, target, XPathConstants.NODESET);
293 } catch (XPathExpressionException e) {
294 reportError((Element) bindings, WsdlMessages.INTERNALIZER_X_PATH_EVALUATION_ERROR(e.getMessage()), e);
295 return null; // abort processing this <jaxb:bindings>
296 }
298 if (nlst.getLength() == 0) {
299 reportError((Element) bindings, WsdlMessages.INTERNALIZER_X_PATH_EVALUATES_TO_NO_TARGET(expression));
300 return null; // abort
301 }
303 return nlst;
304 }
306 private boolean isJAXBBindingElement(Element e) {
307 return fixNull(e.getNamespaceURI()).equals(JAXWSBindingsConstants.NS_JAXB_BINDINGS);
308 }
310 private boolean isJAXWSBindingElement(Element e) {
311 return fixNull(e.getNamespaceURI()).equals(JAXWSBindingsConstants.NS_JAXWS_BINDINGS);
312 }
314 /**
315 * Moves the "decl" node under the "target" node.
316 *
317 * @param decl A JAXWS customization element (e.g., <JAXWS:class>)
318 * @param target XML wsdl element under which the declaration should move.
319 * For example, <xs:element>
320 */
321 private void moveUnder(Element decl, Element target) {
323 //if there is @node on decl and has a child element jaxb:bindings, move it under the target
324 //Element jaxb = getJAXBBindingElement(decl);
325 if (isJAXBBindingElement(decl)) {
326 //add jaxb namespace declaration
327 if (!target.hasAttributeNS(Constants.NS_XMLNS, "jaxb")) {
328 target.setAttributeNS(Constants.NS_XMLNS, "xmlns:jaxb", JAXWSBindingsConstants.NS_JAXB_BINDINGS);
329 }
331 //add jaxb:bindings version info. Lets put it to 1.0, may need to change latter
332 if (!target.hasAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "version")) {
333 target.setAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "jaxb:version", JAXWSBindingsConstants.JAXB_BINDING_VERSION);
334 }
336 // HACK: allow XJC extension all the time. This allows people to specify
337 // the <xjc:someExtension> in the external bindings. Otherwise users lack the ability
338 // to specify jaxb:extensionBindingPrefixes, so it won't work.
339 //
340 // the current workaround is still problematic in the sense that
341 // it can't support user-defined extensions. This needs more careful thought.
343 //JAXB doesn't allow writing jaxb:extensionbindingPrefix anywhere other than root element so lets write only on <xs:schema>
344 if (target.getLocalName().equals("schema") && target.getNamespaceURI().equals(Constants.NS_XSD) && !target.hasAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "extensionBindingPrefixes")) {
345 target.setAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "jaxb:extensionBindingPrefixes", "xjc");
346 target.setAttributeNS(Constants.NS_XMLNS, "xmlns:xjc", JAXWSBindingsConstants.NS_XJC_BINDINGS);
347 }
349 //insert xs:annotation/xs:appinfo where in jaxb:binding will be put
350 target = refineSchemaTarget(target);
351 copyInscopeNSAttributes(decl);
352 } else if (isJAXWSBindingElement(decl)) {
353 //add jaxb namespace declaration
354 if (!target.hasAttributeNS(Constants.NS_XMLNS, "JAXWS")) {
355 target.setAttributeNS(Constants.NS_XMLNS, "xmlns:JAXWS", JAXWSBindingsConstants.NS_JAXWS_BINDINGS);
356 }
358 //insert xs:annotation/xs:appinfo where in jaxb:binding will be put
359 target = refineWSDLTarget(target);
360 copyInscopeNSAttributes(decl);
361 } else {
362 return;
363 }
365 // finally move the declaration to the target node.
366 if (target.getOwnerDocument() != decl.getOwnerDocument()) {
367 // if they belong to different DOM documents, we need to clone them
368 decl = (Element) target.getOwnerDocument().importNode(decl, true);
370 }
372 target.appendChild(decl);
373 }
375 /**
376 * Copy in-scope namespace declarations of the decl node
377 * to the decl node itself so that this move won't change
378 * the in-scope namespace bindings.
379 */
380 private void copyInscopeNSAttributes(Element e) {
381 Element p = e;
382 Set<String> inscopes = new HashSet<String>();
383 while (true) {
384 NamedNodeMap atts = p.getAttributes();
385 for (int i = 0; i < atts.getLength(); i++) {
386 Attr a = (Attr) atts.item(i);
387 if (Constants.NS_XMLNS.equals(a.getNamespaceURI())) {
388 String prefix;
389 if (a.getName().indexOf(':') == -1) {
390 prefix = "";
391 } else {
392 prefix = a.getLocalName();
393 }
395 if (inscopes.add(prefix) && p != e) {
396 // if this is the first time we see this namespace bindings,
397 // copy the declaration.
398 // if p==decl, there's no need to. Note that
399 // we want to add prefix to inscopes even if p==Decl
401 e.setAttributeNodeNS((Attr) a.cloneNode(true));
402 }
403 }
404 }
406 if (p.getParentNode() instanceof Document) {
407 break;
408 }
410 p = (Element) p.getParentNode();
411 }
413 if (!inscopes.contains("")) {
414 // if the default namespace was undeclared in the context of decl,
415 // it must be explicitly set to "" since the new environment might
416 // have a different default namespace URI.
417 e.setAttributeNS(Constants.NS_XMLNS, "xmlns", "");
418 }
419 }
421 public Element refineSchemaTarget(Element target) {
422 // look for existing xs:annotation
423 Element annotation = DOMUtils.getFirstChildElement(target, Constants.NS_XSD, "annotation");
424 if (annotation == null) {
425 // none exists. need to make one
426 annotation = insertXMLSchemaElement(target, "annotation");
427 }
429 // then look for appinfo
430 Element appinfo = DOMUtils.getFirstChildElement(annotation, Constants.NS_XSD, "appinfo");
431 if (appinfo == null) {
432 // none exists. need to make one
433 appinfo = insertXMLSchemaElement(annotation, "appinfo");
434 }
436 return appinfo;
437 }
439 public Element refineWSDLTarget(Element target) {
440 // look for existing xs:annotation
441 Element JAXWSBindings = DOMUtils.getFirstChildElement(target, JAXWSBindingsConstants.NS_JAXWS_BINDINGS, "bindings");
442 if (JAXWSBindings == null) {
443 // none exists. need to make one
444 JAXWSBindings = insertJAXWSBindingsElement(target, "bindings");
445 }
446 return JAXWSBindings;
447 }
449 /**
450 * Creates a new XML Schema element of the given local name
451 * and insert it as the first child of the given parent node.
452 *
453 * @return Newly create element.
454 */
455 private Element insertXMLSchemaElement(Element parent, String localName) {
456 // use the same prefix as the parent node to avoid modifying
457 // the namespace binding.
458 String qname = parent.getTagName();
459 int idx = qname.indexOf(':');
460 if (idx == -1) {
461 qname = localName;
462 } else {
463 qname = qname.substring(0, idx + 1) + localName;
464 }
466 Element child = parent.getOwnerDocument().createElementNS(Constants.NS_XSD, qname);
468 NodeList children = parent.getChildNodes();
470 if (children.getLength() == 0) {
471 parent.appendChild(child);
472 } else {
473 parent.insertBefore(child, children.item(0));
474 }
476 return child;
477 }
479 private Element insertJAXWSBindingsElement(Element parent, String localName) {
480 String qname = "JAXWS:" + localName;
482 Element child = parent.getOwnerDocument().createElementNS(JAXWSBindingsConstants.NS_JAXWS_BINDINGS, qname);
484 NodeList children = parent.getChildNodes();
486 if (children.getLength() == 0) {
487 parent.appendChild(child);
488 } else {
489 parent.insertBefore(child, children.item(0));
490 }
492 return child;
493 }
495 @NotNull
496 static String fixNull(@Nullable String s) {
497 if (s == null) {
498 return "";
499 } else {
500 return s;
501 }
502 }
504 private void reportError(Element errorSource, String formattedMsg) {
505 reportError(errorSource, formattedMsg, null);
506 }
508 private void reportError(Element errorSource,
509 String formattedMsg, Exception nestedException) {
511 SAXParseException e = new SAXParseException2(formattedMsg,
512 forest.locatorTable.getStartLocation(errorSource),
513 nestedException);
514 errorReceiver.error(e);
515 }
518 }