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.processor.modeler.wsdl;
  27 
  28 import com.sun.tools.internal.ws.processor.generator.Names;
  29 import static com.sun.tools.internal.ws.processor.modeler.wsdl.WSDLModelerBase.getExtensionOfType;
  30 import com.sun.tools.internal.ws.wscompile.ErrorReceiver;
  31 import com.sun.tools.internal.ws.wscompile.WsimportOptions;
  32 import com.sun.tools.internal.ws.wscompile.Options;
  33 import com.sun.tools.internal.ws.wsdl.document.*;
  34 import com.sun.tools.internal.ws.wsdl.document.jaxws.JAXWSBinding;
  35 import com.sun.tools.internal.ws.wsdl.document.schema.SchemaKinds;
  36 import com.sun.tools.internal.ws.wsdl.document.soap.SOAP12Binding;
  37 import com.sun.tools.internal.ws.wsdl.document.soap.SOAPBinding;
  38 import org.xml.sax.InputSource;
  39 
  40 import javax.xml.namespace.QName;
  41 import java.io.ByteArrayInputStream;
  42 import java.io.StringReader;
  43 import java.io.StringWriter;
  44 import java.nio.charset.StandardCharsets;
  45 import java.text.MessageFormat;
  46 import java.util.*;
  47 
  48 
  49 /**
  50  * Builds all possible pseudo schemas for async operation ResponseBean to feed to XJC.
  51  *
  52  * @author Vivek Pandey
  53  */
  54 public class PseudoSchemaBuilder {
  55 
  56     private final StringWriter buf = new StringWriter();
  57     private final WSDLDocument wsdlDocument;
  58     private WSDLModeler wsdlModeler;
  59     private final List<InputSource> schemas = new ArrayList<InputSource>();
  60     private final HashMap<QName, Port> bindingNameToPortMap = new HashMap<QName, Port>();
  61     private static final String w3ceprSchemaBinding = "<bindings\n" +
  62             "  xmlns=\"http://java.sun.com/xml/ns/jaxb\"\n" +
  63             "  xmlns:wsa=\"http://www.w3.org/2005/08/addressing\"\n" +
  64             "  xmlns:xjc=\"http://java.sun.com/xml/ns/jaxb/xjc\"\n" +
  65             "  version=\"2.1\">\n" +
  66             "  \n" +
  67             "  <bindings scd=\"x-schema::wsa\" if-exists=\"true\">\n" +
  68    //comment the following, otw JAXB won't generate ObjectFactory, classes from wsa schema. See JAX-WS-804
  69    //         "    <schemaBindings map=\"false\" />\n" +
  70             "    <bindings scd=\"wsa:EndpointReference\">\n" +
  71             "      <class ref=\"javax.xml.ws.wsaddressing.W3CEndpointReference\" xjc:recursive=\"true\"/>\n" +
  72             "    </bindings>\n" +
  73             "    <bindings scd=\"~wsa:EndpointReferenceType\">\n" +
  74             "      <class ref=\"javax.xml.ws.wsaddressing.W3CEndpointReference\" xjc:recursive=\"true\"/>\n" +
  75             "    </bindings>\n" +
  76             "  </bindings>\n" +
  77             "</bindings>";
  78 
  79     private static final String memberSubmissionEPR = "<bindings\n" +
  80             "  xmlns=\"http://java.sun.com/xml/ns/jaxb\"\n" +
  81             "  xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"\n" +
  82             "  version=\"2.1\">\n" +
  83             "  \n" +
  84             "  <bindings scd=\"x-schema::wsa\" if-exists=\"true\">\n" +
  85 //comment the following, otw JAXB won't generate ObjectFactory, classes from wsa schema. See JAX-WS-804
  86 //            "    <schemaBindings map=\"false\" />\n" +
  87             "    <bindings scd=\"wsa:EndpointReference\">\n" +
  88             "      <class ref=\"com.sun.xml.internal.ws.developer.MemberSubmissionEndpointReference\"/>\n" +
  89             "    </bindings>\n" +
  90             "    <bindings scd=\"~wsa:EndpointReferenceType\">\n" +
  91             "      <class ref=\"com.sun.xml.internal.ws.developer.MemberSubmissionEndpointReference\"/>\n" +
  92             "    </bindings>\n" +
  93             "  </bindings>\n" +
  94             "</bindings>";
  95 
  96     private final static String sysId = "http://dummy.pseudo-schema#schema";
  97 
  98     private WsimportOptions options;
  99     public static List<InputSource> build(WSDLModeler wsdlModeler, WsimportOptions options, ErrorReceiver errReceiver) {
 100         PseudoSchemaBuilder b = new PseudoSchemaBuilder(wsdlModeler.document);
 101         b.wsdlModeler = wsdlModeler;
 102         b.options = options;
 103         b.build();
 104         int i;
 105         for(i = 0; i < b.schemas.size(); i++){
 106             InputSource is = b.schemas.get(i);
 107             is.setSystemId(sysId+(i + 1));
 108         }
 109         //add w3c EPR binding
 110         if(!(options.noAddressingBbinding) && options.target.isLaterThan(Options.Target.V2_1)){
 111             InputSource is = new InputSource(new ByteArrayInputStream(w3ceprSchemaBinding.getBytes(StandardCharsets.UTF_8)));
 112             is.setSystemId(sysId+(++i +1));
 113             b.schemas.add(is);
 114         }
 115 
 116 
 117         //TODO: uncomment after JAXB fixes the issue related to passing multiples of such bindings
 118         //add member submission EPR binding
 119 //        InputSource is1 = new InputSource(new ByteArrayInputStream(memberSubmissionEPR.getBytes()));
 120 //        is1.setSystemId(sysId+(++i + 1));
 121 //        b.schemas.add(is1);
 122 
 123         return b.schemas;
 124     }
 125 
 126 
 127     private PseudoSchemaBuilder(WSDLDocument _wsdl) {
 128         this.wsdlDocument = _wsdl;
 129     }
 130 
 131     private void build() {
 132         for(Iterator<Service> itr=wsdlDocument.getDefinitions().services(); itr.hasNext(); ) {
 133             build(itr.next());
 134         }
 135     }
 136 
 137     private void build(Service service) {
 138         for( Iterator<Port> itr=service.ports(); itr.hasNext(); ) {
 139             build(itr.next() );
 140         }
 141     }
 142 
 143     private void build(Port port) {
 144         if(wsdlModeler.isProvider(port))
 145             return;
 146         Binding binding = port.resolveBinding(wsdlDocument);
 147 
 148         SOAPBinding soapBinding =
 149                     (SOAPBinding)getExtensionOfType(binding, SOAPBinding.class);
 150         //lets try and see if its SOAP 1.2. dont worry about extension flag, its
 151         // handled much earlier
 152         if (soapBinding == null) {
 153                     soapBinding =
 154                             (SOAPBinding)getExtensionOfType(binding, SOAP12Binding.class);
 155         }
 156         if(soapBinding == null)
 157             return;
 158         PortType portType = binding.resolvePortType(wsdlDocument);
 159 
 160         QName bindingName = WSDLModelerBase.getQNameOf(binding);
 161 
 162         //we dont want to process the port bound to the binding processed earlier
 163         if(bindingNameToPortMap.containsKey(bindingName))
 164             return;
 165 
 166         bindingNameToPortMap.put(bindingName, port);
 167 
 168 
 169         for(Iterator itr=binding.operations(); itr.hasNext();){
 170             BindingOperation bindingOperation = (BindingOperation)itr.next();
 171 
 172             // get only the bounded operations
 173             Set boundedOps = portType.getOperationsNamed(bindingOperation.getName());
 174             if(boundedOps.size() != 1)
 175                 continue;
 176             Operation operation = (Operation)boundedOps.iterator().next();
 177 
 178             // No pseudo schema required for doc/lit
 179             if(wsdlModeler.isAsync(portType, operation)){
 180                 buildAsync(portType, operation, bindingOperation);
 181             }
 182         }
 183     }
 184 
 185     /**
 186      * @param portType
 187      * @param operation
 188      * @param bindingOperation
 189      */
 190     private void buildAsync(PortType portType, Operation operation, BindingOperation bindingOperation) {
 191         String operationName = getCustomizedOperationName(operation);//operation.getName();
 192         if(operationName == null)
 193             return;
 194         Message outputMessage = null;
 195         if(operation.getOutput() != null)
 196             outputMessage = operation.getOutput().resolveMessage(wsdlDocument);
 197         if(outputMessage != null){
 198             List<MessagePart> allParts = new ArrayList<MessagePart>(outputMessage.getParts());
 199             if(options != null && options.additionalHeaders) {
 200                 List<MessagePart> addtionalHeaderParts = wsdlModeler.getAdditionHeaderParts(bindingOperation, outputMessage, false);
 201                 allParts.addAll(addtionalHeaderParts);
 202             }
 203             if(allParts.size() > 1)
 204                 build(getOperationName(operationName), allParts);
 205         }
 206 
 207     }
 208 
 209     private String getCustomizedOperationName(Operation operation) {
 210         JAXWSBinding jaxwsCustomization = (JAXWSBinding)getExtensionOfType(operation, JAXWSBinding.class);
 211         String operationName = (jaxwsCustomization != null)?((jaxwsCustomization.getMethodName() != null)?jaxwsCustomization.getMethodName().getName():null):null;
 212         if(operationName != null){
 213             if(Names.isJavaReservedWord(operationName)){
 214                 return null;
 215             }
 216 
 217             return operationName;
 218         }
 219         return operation.getName();
 220     }
 221 
 222     private void writeImports(QName elementName, List<MessagePart> parts){
 223         Set<String> uris = new HashSet<String>();
 224         for(MessagePart p:parts){
 225             String ns = p.getDescriptor().getNamespaceURI();
 226             if(!uris.contains(ns) && !ns.equals("http://www.w3.org/2001/XMLSchema") && !ns.equals(elementName.getNamespaceURI())){
 227                 print("<xs:import namespace=''{0}''/>", ns);
 228                 uris.add(ns);
 229             }
 230         }
 231     }
 232 
 233     boolean asyncRespBeanBinding = false;
 234     private void build(QName elementName, List<MessagePart> allParts){
 235 
 236         print(
 237                 "<xs:schema xmlns:xs=''http://www.w3.org/2001/XMLSchema''" +
 238                 "           xmlns:jaxb=''http://java.sun.com/xml/ns/jaxb''" +
 239                 "           xmlns:xjc=''http://java.sun.com/xml/ns/jaxb/xjc''" +
 240                 "           jaxb:extensionBindingPrefixes=''xjc''" +
 241                 "           jaxb:version=''1.0''" +
 242                 "           targetNamespace=''{0}''>",
 243                 elementName.getNamespaceURI());
 244 
 245         writeImports(elementName, allParts);
 246 
 247         if(!asyncRespBeanBinding){
 248             print(
 249                     "<xs:annotation><xs:appinfo>" +
 250                     "  <jaxb:schemaBindings>" +
 251                     "    <jaxb:package name=''{0}'' />" +
 252                     "  </jaxb:schemaBindings>" +
 253                     "</xs:appinfo></xs:annotation>",
 254                     wsdlModeler.getJavaPackage() );
 255             asyncRespBeanBinding = true;
 256         }
 257 
 258         print("<xs:element name=''{0}''>", elementName.getLocalPart());
 259         print("<xs:complexType>");
 260         print("<xs:sequence>");
 261 
 262 
 263         for(MessagePart p:allParts) {
 264             //rpclit wsdl:part must reference schema type not element, also it must exclude headers and mime parts
 265             if(p.getDescriptorKind() == SchemaKinds.XSD_ELEMENT){
 266                 print("<xs:element ref=''types:{0}'' xmlns:types=''{1}''/>",p.getDescriptor().getLocalPart(), p.getDescriptor().getNamespaceURI());
 267             }else{
 268                 print("<xs:element name=''{0}'' type=''{1}'' xmlns=''{2}'' />",
 269                     p.getName(),
 270                     p.getDescriptor().getLocalPart(),
 271                     p.getDescriptor().getNamespaceURI() );
 272             }
 273         }
 274 
 275         print("</xs:sequence>");
 276         print("</xs:complexType>");
 277         print("</xs:element>");
 278         print("</xs:schema>");
 279 
 280         // reset the StringWriter, so that next operation element could be written
 281         if(buf.toString().length() > 0){
 282             //System.out.println("Response bean Schema for operation========> "+ elementName+"\n\n"+buf);
 283             InputSource is = new InputSource(new StringReader(buf.toString()));
 284             schemas.add(is);
 285             buf.getBuffer().setLength(0);
 286         }
 287     }
 288 
 289     private QName getOperationName(String operationName){
 290         if(operationName == null)
 291             return null;
 292 //        String namespaceURI = wsdlDocument.getDefinitions().getTargetNamespaceURI()+"?"+portType.getName()+"?" + operationName;
 293         String namespaceURI = "";
 294         return new QName(namespaceURI, operationName+"Response");
 295     }
 296 
 297     private void print( String msg ) {
 298         print( msg, new Object[0] );
 299     }
 300     private void print( String msg, Object arg1 ) {
 301         print( msg, new Object[]{arg1} );
 302     }
 303     private void print( String msg, Object arg1, Object arg2 ) {
 304         print( msg, new Object[]{arg1, arg2} );
 305     }
 306     private void print( String msg, Object arg1, Object arg2, Object arg3 ) {
 307         print( msg, new Object[]{arg1,arg2,arg3} );
 308     }
 309     private void print( String msg, Object[] args ) {
 310         buf.write(MessageFormat.format(msg,args));
 311         buf.write('\n');
 312     }
 313 
 314 }