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.io.UnsupportedEncodingException;
  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(getUTF8Bytes(w3ceprSchemaBinding)));
 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     private static byte[] getUTF8Bytes(String w3ceprSchemaBinding1) {
 127         try {
 128             return w3ceprSchemaBinding1.getBytes("UTF-8");
 129         } catch (UnsupportedEncodingException unexpected) {
 130             // should never happen
 131             throw new IllegalStateException(unexpected);
 132         }
 133     }
 134 
 135 
 136     private PseudoSchemaBuilder(WSDLDocument _wsdl) {
 137         this.wsdlDocument = _wsdl;
 138     }
 139 
 140     private void build() {
 141         for(Iterator<Service> itr=wsdlDocument.getDefinitions().services(); itr.hasNext(); ) {
 142             build(itr.next());
 143         }
 144     }
 145 
 146     private void build(Service service) {
 147         for( Iterator<Port> itr=service.ports(); itr.hasNext(); ) {
 148             build(itr.next() );
 149         }
 150     }
 151 
 152     private void build(Port port) {
 153         if(wsdlModeler.isProvider(port))
 154             return;
 155         Binding binding = port.resolveBinding(wsdlDocument);
 156 
 157         SOAPBinding soapBinding =
 158                     (SOAPBinding)getExtensionOfType(binding, SOAPBinding.class);
 159         //lets try and see if its SOAP 1.2. dont worry about extension flag, its
 160         // handled much earlier
 161         if (soapBinding == null) {
 162                     soapBinding =
 163                             (SOAPBinding)getExtensionOfType(binding, SOAP12Binding.class);
 164         }
 165         if(soapBinding == null)
 166             return;
 167         PortType portType = binding.resolvePortType(wsdlDocument);
 168 
 169         QName bindingName = WSDLModelerBase.getQNameOf(binding);
 170 
 171         //we dont want to process the port bound to the binding processed earlier
 172         if(bindingNameToPortMap.containsKey(bindingName))
 173             return;
 174 
 175         bindingNameToPortMap.put(bindingName, port);
 176 
 177 
 178         for(Iterator itr=binding.operations(); itr.hasNext();){
 179             BindingOperation bindingOperation = (BindingOperation)itr.next();
 180 
 181             // get only the bounded operations
 182             Set boundedOps = portType.getOperationsNamed(bindingOperation.getName());
 183             if(boundedOps.size() != 1)
 184                 continue;
 185             Operation operation = (Operation)boundedOps.iterator().next();
 186 
 187             // No pseudo schema required for doc/lit
 188             if(wsdlModeler.isAsync(portType, operation)){
 189                 buildAsync(portType, operation, bindingOperation);
 190             }
 191         }
 192     }
 193 
 194     /**
 195      * @param portType
 196      * @param operation
 197      * @param bindingOperation
 198      */
 199     private void buildAsync(PortType portType, Operation operation, BindingOperation bindingOperation) {
 200         String operationName = getCustomizedOperationName(operation);//operation.getName();
 201         if(operationName == null)
 202             return;
 203         Message outputMessage = null;
 204         if(operation.getOutput() != null)
 205             outputMessage = operation.getOutput().resolveMessage(wsdlDocument);
 206         if(outputMessage != null){
 207             List<MessagePart> allParts = new ArrayList<MessagePart>(outputMessage.getParts());
 208             if(options != null && options.additionalHeaders) {
 209                 List<MessagePart> addtionalHeaderParts = wsdlModeler.getAdditionHeaderParts(bindingOperation, outputMessage, false);
 210                 allParts.addAll(addtionalHeaderParts);
 211             }
 212             if(allParts.size() > 1)
 213                 build(getOperationName(operationName), allParts);
 214         }
 215 
 216     }
 217 
 218     private String getCustomizedOperationName(Operation operation) {
 219         JAXWSBinding jaxwsCustomization = (JAXWSBinding)getExtensionOfType(operation, JAXWSBinding.class);
 220         String operationName = (jaxwsCustomization != null)?((jaxwsCustomization.getMethodName() != null)?jaxwsCustomization.getMethodName().getName():null):null;
 221         if(operationName != null){
 222             if(Names.isJavaReservedWord(operationName)){
 223                 return null;
 224             }
 225 
 226             return operationName;
 227         }
 228         return operation.getName();
 229     }
 230 
 231     private void writeImports(QName elementName, List<MessagePart> parts){
 232         Set<String> uris = new HashSet<String>();
 233         for(MessagePart p:parts){
 234             String ns = p.getDescriptor().getNamespaceURI();
 235             if(!uris.contains(ns) && !ns.equals("http://www.w3.org/2001/XMLSchema") && !ns.equals(elementName.getNamespaceURI())){
 236                 print("<xs:import namespace=''{0}''/>", ns);
 237                 uris.add(ns);
 238             }
 239         }
 240     }
 241 
 242     boolean asyncRespBeanBinding = false;
 243     private void build(QName elementName, List<MessagePart> allParts){
 244 
 245         print(
 246                 "<xs:schema xmlns:xs=''http://www.w3.org/2001/XMLSchema''" +
 247                 "           xmlns:jaxb=''http://java.sun.com/xml/ns/jaxb''" +
 248                 "           xmlns:xjc=''http://java.sun.com/xml/ns/jaxb/xjc''" +
 249                 "           jaxb:extensionBindingPrefixes=''xjc''" +
 250                 "           jaxb:version=''1.0''" +
 251                 "           targetNamespace=''{0}''>",
 252                 elementName.getNamespaceURI());
 253 
 254         writeImports(elementName, allParts);
 255 
 256         if(!asyncRespBeanBinding){
 257             print(
 258                     "<xs:annotation><xs:appinfo>" +
 259                     "  <jaxb:schemaBindings>" +
 260                     "    <jaxb:package name=''{0}'' />" +
 261                     "  </jaxb:schemaBindings>" +
 262                     "</xs:appinfo></xs:annotation>",
 263                     wsdlModeler.getJavaPackage() );
 264             asyncRespBeanBinding = true;
 265         }
 266 
 267         print("<xs:element name=''{0}''>", elementName.getLocalPart());
 268         print("<xs:complexType>");
 269         print("<xs:sequence>");
 270 
 271 
 272         for(MessagePart p:allParts) {
 273             //rpclit wsdl:part must reference schema type not element, also it must exclude headers and mime parts
 274             if(p.getDescriptorKind() == SchemaKinds.XSD_ELEMENT){
 275                 print("<xs:element ref=''types:{0}'' xmlns:types=''{1}''/>",p.getDescriptor().getLocalPart(), p.getDescriptor().getNamespaceURI());
 276             }else{
 277                 print("<xs:element name=''{0}'' type=''{1}'' xmlns=''{2}'' />",
 278                     p.getName(),
 279                     p.getDescriptor().getLocalPart(),
 280                     p.getDescriptor().getNamespaceURI() );
 281             }
 282         }
 283 
 284         print("</xs:sequence>");
 285         print("</xs:complexType>");
 286         print("</xs:element>");
 287         print("</xs:schema>");
 288 
 289         // reset the StringWriter, so that next operation element could be written
 290         if(buf.toString().length() > 0){
 291             //System.out.println("Response bean Schema for operation========> "+ elementName+"\n\n"+buf);
 292             InputSource is = new InputSource(new StringReader(buf.toString()));
 293             schemas.add(is);
 294             buf.getBuffer().setLength(0);
 295         }
 296     }
 297 
 298     private QName getOperationName(String operationName){
 299         if(operationName == null)
 300             return null;
 301 //        String namespaceURI = wsdlDocument.getDefinitions().getTargetNamespaceURI()+"?"+portType.getName()+"?" + operationName;
 302         String namespaceURI = "";
 303         return new QName(namespaceURI, operationName+"Response");
 304     }
 305 
 306     private void print( String msg ) {
 307         print( msg, new Object[0] );
 308     }
 309     private void print( String msg, Object arg1 ) {
 310         print( msg, new Object[]{arg1} );
 311     }
 312     private void print( String msg, Object arg1, Object arg2 ) {
 313         print( msg, new Object[]{arg1, arg2} );
 314     }
 315     private void print( String msg, Object arg1, Object arg2, Object arg3 ) {
 316         print( msg, new Object[]{arg1,arg2,arg3} );
 317     }
 318     private void print( String msg, Object[] args ) {
 319         buf.write(MessageFormat.format(msg,args));
 320         buf.write('\n');
 321     }
 322 
 323 }