src/share/jaxws_classes/com/sun/tools/internal/ws/wsdl/parser/Internalizer.java

changeset 0
373ffda63c9a
child 637
9c07ef4934dd
equal deleted inserted replaced
-1:000000000000 0:373ffda63c9a
1 /*
2 * Copyright (c) 1997, 2014, 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 */
25
26 package com.sun.tools.internal.ws.wsdl.parser;
27
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;
42
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;
54
55
56 /**
57 * Internalizes external binding declarations.
58 *
59 * @author Vivek Pandey
60 */
61 public class Internalizer {
62
63 private final XPath xpath = xpf.get().newXPath();
64 private final DOMForest forest;
65 private final ErrorReceiver errorReceiver;
66
67 public Internalizer(DOMForest forest, WsimportOptions options, ErrorReceiver errorReceiver) {
68 this.forest = forest;
69 this.errorReceiver = errorReceiver;
70 }
71
72 public void transform() {
73 for (Element jaxwsBinding : forest.outerMostBindings) {
74 internalize(jaxwsBinding, jaxwsBinding);
75 }
76 }
77
78 private static final ContextClassloaderLocal<XPathFactory> xpf = new ContextClassloaderLocal<XPathFactory>() {
79 @Override
80 protected XPathFactory initialValue() throws Exception {
81 return XPathFactory.newInstance();
82 }
83 };
84 /**
85 * Validates attributes of a &lt;JAXWS:bindings> element.
86 */
87 private void validate(Element bindings) {
88 NamedNodeMap atts = bindings.getAttributes();
89 for (int i = 0; i < atts.getLength(); i++) {
90 Attr a = (Attr) atts.item(i);
91 if (a.getNamespaceURI() != null) {
92 continue; // all foreign namespace OK.
93 }
94 if (a.getLocalName().equals("node")) {
95 continue;
96 }
97 if (a.getLocalName().equals("wsdlLocation")) {
98 continue;
99 }
100
101 // TODO: flag error for this undefined attribute
102 }
103 }
104
105 private void internalize(Element bindings, Node inheritedTarget) {
106 // start by the inherited target
107 Node target = inheritedTarget;
108
109 validate(bindings); // validate this node
110
111 // look for @wsdlLocation
112 if (isTopLevelBinding(bindings)) {
113 String wsdlLocation;
114 if (bindings.getAttributeNode("wsdlLocation") != null) {
115 wsdlLocation = bindings.getAttribute("wsdlLocation");
116
117 try {
118 // absolutize this URI.
119 // TODO: use the URI class
120 // TODO: honor xml:base
121 wsdlLocation = new URL(new URL(forest.getSystemId(bindings.getOwnerDocument())),
122 wsdlLocation).toExternalForm();
123 } catch (MalformedURLException e) {
124 wsdlLocation = JAXWSUtils.absolutize(JAXWSUtils.getFileOrURLName(wsdlLocation));
125 }
126 } else {
127 //the node does not have
128 wsdlLocation = forest.getFirstRootDocument();
129 }
130 target = forest.get(wsdlLocation);
131
132 if (target == null) {
133 reportError(bindings, WsdlMessages.INTERNALIZER_INCORRECT_SCHEMA_REFERENCE(wsdlLocation, EditDistance.findNearest(wsdlLocation, forest.listSystemIDs())));
134 return; // abort processing this <JAXWS:bindings>
135 }
136 }
137
138 //if the target node is xs:schema, declare the jaxb version on it as latter on it will be
139 //required by the inlined schema bindings
140
141 Element element = DOMUtil.getFirstElementChild(target);
142 if (element != null && element.getNamespaceURI().equals(Constants.NS_WSDL) && element.getLocalName().equals("definitions")) {
143 //get all schema elements
144 Element type = DOMUtils.getFirstChildElement(element, Constants.NS_WSDL, "types");
145 if (type != null) {
146 for (Element schemaElement : DOMUtils.getChildElements(type, Constants.NS_XSD, "schema")) {
147 if (!schemaElement.hasAttributeNS(Constants.NS_XMLNS, "jaxb")) {
148 schemaElement.setAttributeNS(Constants.NS_XMLNS, "xmlns:jaxb", JAXWSBindingsConstants.NS_JAXB_BINDINGS);
149 }
150
151 //add jaxb:bindings version info. Lets put it to 1.0, may need to change latter
152 if (!schemaElement.hasAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "version")) {
153 schemaElement.setAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "jaxb:version", JAXWSBindingsConstants.JAXB_BINDING_VERSION);
154 }
155 }
156 }
157 }
158
159
160 NodeList targetNodes = null;
161 boolean hasNode = true;
162 boolean isToplevelBinding = isTopLevelBinding(bindings);
163 if ((isJAXWSBindings(bindings) || isJAXBBindings(bindings)) && bindings.getAttributeNode("node") != null) {
164 targetNodes = evaluateXPathMultiNode(bindings, target, bindings.getAttribute("node"), new NamespaceContextImpl(bindings));
165 } else
166 if (isJAXWSBindings(bindings) && (bindings.getAttributeNode("node") == null) && !isToplevelBinding) {
167 hasNode = false;
168 } else
169 if (isGlobalBinding(bindings) && !isWSDLDefinition(target) && isTopLevelBinding(bindings.getParentNode())) {
170 targetNodes = getWSDLDefintionNode(bindings, target);
171 }
172
173 //if target is null or empty it means the xpath evaluation has some problem,
174 // just return
175 if (targetNodes == null && hasNode && !isToplevelBinding) {
176 return;
177 }
178
179 if (hasNode) {
180 if (targetNodes != null) {
181 for (int i = 0; i < targetNodes.getLength(); i++) {
182 insertBinding(bindings, targetNodes.item(i));
183 // look for child <JAXWS:bindings> and process them recursively
184 Element[] children = getChildElements(bindings);
185 for (Element child : children) {
186 if ("bindings".equals(child.getLocalName())) {
187 internalize(child, targetNodes.item(i));
188 }
189 }
190 }
191 }
192 }
193 if (targetNodes == null) {
194 // look for child <JAXWS:bindings> and process them recursively
195 Element[] children = getChildElements(bindings);
196
197 for (Element child : children) {
198 internalize(child, target);
199 }
200 }
201 }
202
203 /**
204 * Moves JAXWS customizations under their respective target nodes.
205 */
206 private void insertBinding(@NotNull Element bindings, @NotNull Node target) {
207 if ("bindings".equals(bindings.getLocalName())) {
208 Element[] children = DOMUtils.getChildElements(bindings);
209 for (Element item : children) {
210 if ("bindings".equals(item.getLocalName())) {
211 //done
212 } else {
213 moveUnder(item, (Element) target);
214
215 }
216 }
217 } else {
218 moveUnder(bindings, (Element) target);
219 }
220 }
221
222 private NodeList getWSDLDefintionNode(Node bindings, Node target) {
223 return evaluateXPathMultiNode(bindings, target, "wsdl:definitions",
224 new NamespaceContext() {
225 @Override
226 public String getNamespaceURI(String prefix) {
227 return "http://schemas.xmlsoap.org/wsdl/";
228 }
229
230 @Override
231 public String getPrefix(String nsURI) {
232 throw new UnsupportedOperationException();
233 }
234
235 @Override
236 public Iterator getPrefixes(String namespaceURI) {
237 throw new UnsupportedOperationException();
238 }
239 });
240 }
241
242 private boolean isWSDLDefinition(Node target) {
243 if (target == null) {
244 return false;
245 }
246 String localName = target.getLocalName();
247 String nsURI = target.getNamespaceURI();
248 return fixNull(localName).equals("definitions") && fixNull(nsURI).equals("http://schemas.xmlsoap.org/wsdl/");
249 }
250
251 private boolean isTopLevelBinding(Node node) {
252 return node.getOwnerDocument().getDocumentElement() == node;
253 }
254
255 private boolean isJAXWSBindings(Node bindings) {
256 return (bindings.getNamespaceURI().equals(JAXWSBindingsConstants.NS_JAXWS_BINDINGS) && bindings.getLocalName().equals("bindings"));
257 }
258
259 private boolean isJAXBBindings(Node bindings) {
260 return (bindings.getNamespaceURI().equals(JAXWSBindingsConstants.NS_JAXB_BINDINGS) && bindings.getLocalName().equals("bindings"));
261 }
262
263 private boolean isGlobalBinding(Node bindings) {
264 if (bindings.getNamespaceURI() == null) {
265 errorReceiver.warning(forest.locatorTable.getStartLocation((Element) bindings), WsdlMessages.INVALID_CUSTOMIZATION_NAMESPACE(bindings.getLocalName()));
266 return false;
267 }
268 return (bindings.getNamespaceURI().equals(JAXWSBindingsConstants.NS_JAXWS_BINDINGS) &&
269 (bindings.getLocalName().equals("package") ||
270 bindings.getLocalName().equals("enableAsyncMapping") ||
271 bindings.getLocalName().equals("enableAdditionalSOAPHeaderMapping") ||
272 bindings.getLocalName().equals("enableWrapperStyle") ||
273 bindings.getLocalName().equals("enableMIMEContent")));
274 }
275
276 private static Element[] getChildElements(Element parent) {
277 ArrayList<Element> a = new ArrayList<Element>();
278 NodeList children = parent.getChildNodes();
279 for (int i = 0; i < children.getLength(); i++) {
280 Node item = children.item(i);
281 if (!(item instanceof Element)) {
282 continue;
283 }
284 if (JAXWSBindingsConstants.NS_JAXWS_BINDINGS.equals(item.getNamespaceURI()) ||
285 JAXWSBindingsConstants.NS_JAXB_BINDINGS.equals(item.getNamespaceURI())) {
286 a.add((Element) item);
287 }
288 }
289 return a.toArray(new Element[a.size()]);
290 }
291
292 private NodeList evaluateXPathMultiNode(Node bindings, Node target, String expression, NamespaceContext namespaceContext) {
293 NodeList nlst;
294 try {
295 xpath.setNamespaceContext(namespaceContext);
296 nlst = (NodeList) xpath.evaluate(expression, target, XPathConstants.NODESET);
297 } catch (XPathExpressionException e) {
298 reportError((Element) bindings, WsdlMessages.INTERNALIZER_X_PATH_EVALUATION_ERROR(e.getMessage()), e);
299 return null; // abort processing this <jaxb:bindings>
300 }
301
302 if (nlst.getLength() == 0) {
303 reportError((Element) bindings, WsdlMessages.INTERNALIZER_X_PATH_EVALUATES_TO_NO_TARGET(expression));
304 return null; // abort
305 }
306
307 return nlst;
308 }
309
310 private boolean isJAXBBindingElement(Element e) {
311 return fixNull(e.getNamespaceURI()).equals(JAXWSBindingsConstants.NS_JAXB_BINDINGS);
312 }
313
314 private boolean isJAXWSBindingElement(Element e) {
315 return fixNull(e.getNamespaceURI()).equals(JAXWSBindingsConstants.NS_JAXWS_BINDINGS);
316 }
317
318 /**
319 * Moves the "decl" node under the "target" node.
320 *
321 * @param decl A JAXWS customization element (e.g., &lt;JAXWS:class>)
322 * @param target XML wsdl element under which the declaration should move.
323 * For example, &lt;xs:element>
324 */
325 private void moveUnder(Element decl, Element target) {
326
327 //if there is @node on decl and has a child element jaxb:bindings, move it under the target
328 //Element jaxb = getJAXBBindingElement(decl);
329 if (isJAXBBindingElement(decl)) {
330 //add jaxb namespace declaration
331 if (!target.hasAttributeNS(Constants.NS_XMLNS, "jaxb")) {
332 target.setAttributeNS(Constants.NS_XMLNS, "xmlns:jaxb", JAXWSBindingsConstants.NS_JAXB_BINDINGS);
333 }
334
335 //add jaxb:bindings version info. Lets put it to 1.0, may need to change latter
336 if (!target.hasAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "version")) {
337 target.setAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "jaxb:version", JAXWSBindingsConstants.JAXB_BINDING_VERSION);
338 }
339
340 // HACK: allow XJC extension all the time. This allows people to specify
341 // the <xjc:someExtension> in the external bindings. Otherwise users lack the ability
342 // to specify jaxb:extensionBindingPrefixes, so it won't work.
343 //
344 // the current workaround is still problematic in the sense that
345 // it can't support user-defined extensions. This needs more careful thought.
346
347 //JAXB doesn't allow writing jaxb:extensionbindingPrefix anywhere other than root element so lets write only on <xs:schema>
348 if (target.getLocalName().equals("schema") && target.getNamespaceURI().equals(Constants.NS_XSD) && !target.hasAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "extensionBindingPrefixes")) {
349 target.setAttributeNS(JAXWSBindingsConstants.NS_JAXB_BINDINGS, "jaxb:extensionBindingPrefixes", "xjc");
350 target.setAttributeNS(Constants.NS_XMLNS, "xmlns:xjc", JAXWSBindingsConstants.NS_XJC_BINDINGS);
351 }
352
353 //insert xs:annotation/xs:appinfo where in jaxb:binding will be put
354 target = refineSchemaTarget(target);
355 copyInscopeNSAttributes(decl);
356 } else if (isJAXWSBindingElement(decl)) {
357 //add jaxb namespace declaration
358 if (!target.hasAttributeNS(Constants.NS_XMLNS, "JAXWS")) {
359 target.setAttributeNS(Constants.NS_XMLNS, "xmlns:JAXWS", JAXWSBindingsConstants.NS_JAXWS_BINDINGS);
360 }
361
362 //insert xs:annotation/xs:appinfo where in jaxb:binding will be put
363 target = refineWSDLTarget(target);
364 copyInscopeNSAttributes(decl);
365 } else {
366 return;
367 }
368
369 // finally move the declaration to the target node.
370 if (target.getOwnerDocument() != decl.getOwnerDocument()) {
371 // if they belong to different DOM documents, we need to clone them
372 decl = (Element) target.getOwnerDocument().importNode(decl, true);
373
374 }
375
376 target.appendChild(decl);
377 }
378
379 /**
380 * Copy in-scope namespace declarations of the decl node
381 * to the decl node itself so that this move won't change
382 * the in-scope namespace bindings.
383 */
384 private void copyInscopeNSAttributes(Element e) {
385 Element p = e;
386 Set<String> inscopes = new HashSet<String>();
387 while (true) {
388 NamedNodeMap atts = p.getAttributes();
389 for (int i = 0; i < atts.getLength(); i++) {
390 Attr a = (Attr) atts.item(i);
391 if (Constants.NS_XMLNS.equals(a.getNamespaceURI())) {
392 String prefix;
393 if (a.getName().indexOf(':') == -1) {
394 prefix = "";
395 } else {
396 prefix = a.getLocalName();
397 }
398
399 if (inscopes.add(prefix) && p != e) {
400 // if this is the first time we see this namespace bindings,
401 // copy the declaration.
402 // if p==decl, there's no need to. Note that
403 // we want to add prefix to inscopes even if p==Decl
404
405 e.setAttributeNodeNS((Attr) a.cloneNode(true));
406 }
407 }
408 }
409
410 if (p.getParentNode() instanceof Document) {
411 break;
412 }
413
414 p = (Element) p.getParentNode();
415 }
416
417 if (!inscopes.contains("")) {
418 // if the default namespace was undeclared in the context of decl,
419 // it must be explicitly set to "" since the new environment might
420 // have a different default namespace URI.
421 e.setAttributeNS(Constants.NS_XMLNS, "xmlns", "");
422 }
423 }
424
425 public Element refineSchemaTarget(Element target) {
426 // look for existing xs:annotation
427 Element annotation = DOMUtils.getFirstChildElement(target, Constants.NS_XSD, "annotation");
428 if (annotation == null) {
429 // none exists. need to make one
430 annotation = insertXMLSchemaElement(target, "annotation");
431 }
432
433 // then look for appinfo
434 Element appinfo = DOMUtils.getFirstChildElement(annotation, Constants.NS_XSD, "appinfo");
435 if (appinfo == null) {
436 // none exists. need to make one
437 appinfo = insertXMLSchemaElement(annotation, "appinfo");
438 }
439
440 return appinfo;
441 }
442
443 public Element refineWSDLTarget(Element target) {
444 // look for existing xs:annotation
445 Element JAXWSBindings = DOMUtils.getFirstChildElement(target, JAXWSBindingsConstants.NS_JAXWS_BINDINGS, "bindings");
446 if (JAXWSBindings == null) {
447 // none exists. need to make one
448 JAXWSBindings = insertJAXWSBindingsElement(target, "bindings");
449 }
450 return JAXWSBindings;
451 }
452
453 /**
454 * Creates a new XML Schema element of the given local name
455 * and insert it as the first child of the given parent node.
456 *
457 * @return Newly create element.
458 */
459 private Element insertXMLSchemaElement(Element parent, String localName) {
460 // use the same prefix as the parent node to avoid modifying
461 // the namespace binding.
462 String qname = parent.getTagName();
463 int idx = qname.indexOf(':');
464 if (idx == -1) {
465 qname = localName;
466 } else {
467 qname = qname.substring(0, idx + 1) + localName;
468 }
469
470 Element child = parent.getOwnerDocument().createElementNS(Constants.NS_XSD, qname);
471
472 NodeList children = parent.getChildNodes();
473
474 if (children.getLength() == 0) {
475 parent.appendChild(child);
476 } else {
477 parent.insertBefore(child, children.item(0));
478 }
479
480 return child;
481 }
482
483 private Element insertJAXWSBindingsElement(Element parent, String localName) {
484 String qname = "JAXWS:" + localName;
485
486 Element child = parent.getOwnerDocument().createElementNS(JAXWSBindingsConstants.NS_JAXWS_BINDINGS, qname);
487
488 NodeList children = parent.getChildNodes();
489
490 if (children.getLength() == 0) {
491 parent.appendChild(child);
492 } else {
493 parent.insertBefore(child, children.item(0));
494 }
495
496 return child;
497 }
498
499 @NotNull
500 static String fixNull(@Nullable String s) {
501 if (s == null) {
502 return "";
503 } else {
504 return s;
505 }
506 }
507
508 private void reportError(Element errorSource, String formattedMsg) {
509 reportError(errorSource, formattedMsg, null);
510 }
511
512 private void reportError(Element errorSource,
513 String formattedMsg, Exception nestedException) {
514
515 SAXParseException e = new SAXParseException2(formattedMsg,
516 forest.locatorTable.getStartLocation(errorSource),
517 nestedException);
518 errorReceiver.error(e);
519 }
520
521
522 }

mercurial