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

Fri, 04 Oct 2013 16:21:34 +0100

author
mkos
date
Fri, 04 Oct 2013 16:21:34 +0100
changeset 408
b0610cd08440
parent 368
0989ad8c0860
child 515
6cd506508147
permissions
-rw-r--r--

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., &lt;JAXWS:class>)
   318      * @param target XML wsdl element under which the declaration should move.
   319      *               For example, &lt;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 }

mercurial