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

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 515
6cd506508147
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

     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  */
    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 final XPath xpath = xpf.get().newXPath();
    64     private final DOMForest forest;
    65     private final ErrorReceiver errorReceiver;
    67     public Internalizer(DOMForest forest, WsimportOptions options, ErrorReceiver errorReceiver) {
    68         this.forest = forest;
    69         this.errorReceiver = errorReceiver;
    70     }
    72     public void transform() {
    73         for (Element jaxwsBinding : forest.outerMostBindings) {
    74             internalize(jaxwsBinding, jaxwsBinding);
    75         }
    76     }
    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             }
   101             // TODO: flag error for this undefined attribute
   102         }
   103     }
   105     private void internalize(Element bindings, Node inheritedTarget) {
   106         // start by the inherited target
   107         Node target = inheritedTarget;
   109         validate(bindings); // validate this node
   111         // look for @wsdlLocation
   112         if (isTopLevelBinding(bindings)) {
   113             String wsdlLocation;
   114             if (bindings.getAttributeNode("wsdlLocation") != null) {
   115                 wsdlLocation = bindings.getAttribute("wsdlLocation");
   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);
   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         }
   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
   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                     }
   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         }
   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         }
   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         }
   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);
   197             for (Element child : children) {
   198                 internalize(child, target);
   199             }
   200         }
   201     }
   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);
   215                 }
   216             }
   217         } else {
   218             moveUnder(bindings, (Element) target);
   219         }
   220     }
   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                     }
   230                     @Override
   231                     public String getPrefix(String nsURI) {
   232                         throw new UnsupportedOperationException();
   233                     }
   235                     @Override
   236                     public Iterator getPrefixes(String namespaceURI) {
   237                         throw new UnsupportedOperationException();
   238                     }
   239                 });
   240     }
   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     }
   251     private boolean isTopLevelBinding(Node node) {
   252         return node.getOwnerDocument().getDocumentElement() == node;
   253     }
   255     private boolean isJAXWSBindings(Node bindings) {
   256         return (bindings.getNamespaceURI().equals(JAXWSBindingsConstants.NS_JAXWS_BINDINGS) && bindings.getLocalName().equals("bindings"));
   257     }
   259     private boolean isJAXBBindings(Node bindings) {
   260         return (bindings.getNamespaceURI().equals(JAXWSBindingsConstants.NS_JAXB_BINDINGS) && bindings.getLocalName().equals("bindings"));
   261     }
   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     }
   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     }
   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         }
   302         if (nlst.getLength() == 0) {
   303             reportError((Element) bindings, WsdlMessages.INTERNALIZER_X_PATH_EVALUATES_TO_NO_TARGET(expression));
   304             return null; // abort
   305         }
   307         return nlst;
   308     }
   310     private boolean isJAXBBindingElement(Element e) {
   311         return fixNull(e.getNamespaceURI()).equals(JAXWSBindingsConstants.NS_JAXB_BINDINGS);
   312     }
   314     private boolean isJAXWSBindingElement(Element e) {
   315         return fixNull(e.getNamespaceURI()).equals(JAXWSBindingsConstants.NS_JAXWS_BINDINGS);
   316     }
   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) {
   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             }
   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             }
   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.
   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             }
   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             }
   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         }
   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);
   374         }
   376         target.appendChild(decl);
   377     }
   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                     }
   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
   405                         e.setAttributeNodeNS((Attr) a.cloneNode(true));
   406                     }
   407                 }
   408             }
   410             if (p.getParentNode() instanceof Document) {
   411                 break;
   412             }
   414             p = (Element) p.getParentNode();
   415         }
   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     }
   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         }
   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         }
   440         return appinfo;
   441     }
   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     }
   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         }
   470         Element child = parent.getOwnerDocument().createElementNS(Constants.NS_XSD, qname);
   472         NodeList children = parent.getChildNodes();
   474         if (children.getLength() == 0) {
   475             parent.appendChild(child);
   476         } else {
   477             parent.insertBefore(child, children.item(0));
   478         }
   480         return child;
   481     }
   483     private Element insertJAXWSBindingsElement(Element parent, String localName) {
   484         String qname = "JAXWS:" + localName;
   486         Element child = parent.getOwnerDocument().createElementNS(JAXWSBindingsConstants.NS_JAXWS_BINDINGS, qname);
   488         NodeList children = parent.getChildNodes();
   490         if (children.getLength() == 0) {
   491             parent.appendChild(child);
   492         } else {
   493             parent.insertBefore(child, children.item(0));
   494         }
   496         return child;
   497     }
   499     @NotNull
   500     static String fixNull(@Nullable String s) {
   501         if (s == null) {
   502             return "";
   503         } else {
   504             return s;
   505         }
   506     }
   508     private void reportError(Element errorSource, String formattedMsg) {
   509         reportError(errorSource, formattedMsg, null);
   510     }
   512     private void reportError(Element errorSource,
   513                              String formattedMsg, Exception nestedException) {
   515         SAXParseException e = new SAXParseException2(formattedMsg,
   516                 forest.locatorTable.getStartLocation(errorSource),
   517                 nestedException);
   518         errorReceiver.error(e);
   519     }
   522 }

mercurial