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