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.generator;
  27 
  28 import com.sun.codemodel.internal.ClassType;
  29 import com.sun.codemodel.internal.JAnnotationUse;
  30 import com.sun.codemodel.internal.JBlock;
  31 import com.sun.codemodel.internal.JCatchBlock;
  32 import com.sun.codemodel.internal.JClass;
  33 import com.sun.codemodel.internal.JClassAlreadyExistsException;
  34 import com.sun.codemodel.internal.JCommentPart;
  35 import com.sun.codemodel.internal.JConditional;
  36 import com.sun.codemodel.internal.JDefinedClass;
  37 import com.sun.codemodel.internal.JDocComment;
  38 import com.sun.codemodel.internal.JExpr;
  39 import com.sun.codemodel.internal.JFieldVar;
  40 import com.sun.codemodel.internal.JInvocation;
  41 import com.sun.codemodel.internal.JMethod;
  42 import com.sun.codemodel.internal.JMod;
  43 import com.sun.codemodel.internal.JTryBlock;
  44 import com.sun.codemodel.internal.JType;
  45 import com.sun.codemodel.internal.JVar;
  46 import com.sun.tools.internal.ws.processor.model.Model;
  47 import com.sun.tools.internal.ws.processor.model.ModelProperties;
  48 import com.sun.tools.internal.ws.processor.model.Port;
  49 import com.sun.tools.internal.ws.processor.model.Service;
  50 import com.sun.tools.internal.ws.processor.model.java.JavaInterface;
  51 import com.sun.tools.internal.ws.resources.GeneratorMessages;
  52 import com.sun.tools.internal.ws.wscompile.ErrorReceiver;
  53 import com.sun.tools.internal.ws.wscompile.Options;
  54 import com.sun.tools.internal.ws.wscompile.WsimportOptions;
  55 import com.sun.tools.internal.ws.wsdl.document.PortType;
  56 import com.sun.xml.internal.ws.spi.db.BindingHelper;
  57 
  58 import org.xml.sax.Locator;
  59 
  60 import javax.xml.namespace.QName;
  61 import javax.xml.ws.WebEndpoint;
  62 import javax.xml.ws.WebServiceClient;
  63 import javax.xml.ws.WebServiceFeature;
  64 import javax.xml.ws.WebServiceException;
  65 import java.net.MalformedURLException;
  66 import java.net.URL;
  67 
  68 import com.sun.xml.internal.ws.util.ServiceFinder;
  69 import java.util.Locale;
  70 
  71 /**
  72  * @author WS Development Team
  73  * @author Jitendra Kotamraju
  74  */
  75 public class ServiceGenerator extends GeneratorBase {
  76 
  77     public static void generate(Model model, WsimportOptions options, ErrorReceiver receiver) {
  78         ServiceGenerator serviceGenerator = new ServiceGenerator(model, options, receiver);
  79         serviceGenerator.doGeneration();
  80     }
  81 
  82     private ServiceGenerator(Model model, WsimportOptions options, ErrorReceiver receiver) {
  83         init(model, options, receiver);
  84     }
  85 
  86     @Override
  87     public void visit(Service service) {
  88         JavaInterface intf = service.getJavaInterface();
  89         String className = Names.customJavaTypeClassName(intf);
  90         if (donotOverride && GeneratorUtil.classExists(options, className)) {
  91             log("Class " + className + " exists. Not overriding.");
  92             return;
  93         }
  94 
  95         JDefinedClass cls;
  96         try {
  97             cls = getClass(className, ClassType.CLASS);
  98         } catch (JClassAlreadyExistsException e) {
  99             receiver.error(service.getLocator(), GeneratorMessages.GENERATOR_SERVICE_CLASS_ALREADY_EXIST(className, service.getName()));
 100             return;
 101         }
 102 
 103         cls._extends(javax.xml.ws.Service.class);
 104         String serviceFieldName = BindingHelper.mangleNameToClassName(service.getName().getLocalPart()).toUpperCase(Locale.ENGLISH);
 105         String wsdlLocationName = serviceFieldName + "_WSDL_LOCATION";
 106         JFieldVar urlField = cls.field(JMod.PRIVATE | JMod.STATIC | JMod.FINAL, URL.class, wsdlLocationName);
 107 
 108         JFieldVar exField = cls.field(JMod.PRIVATE | JMod.STATIC | JMod.FINAL, WebServiceException.class, serviceFieldName+"_EXCEPTION");
 109 
 110 
 111         String serviceName = serviceFieldName + "_QNAME";
 112         cls.field(JMod.PRIVATE | JMod.STATIC | JMod.FINAL, QName.class, serviceName,
 113             JExpr._new(cm.ref(QName.class)).arg(service.getName().getNamespaceURI()).arg(service.getName().getLocalPart()));
 114 
 115         JClass qNameCls = cm.ref(QName.class);
 116         JInvocation inv;
 117         inv = JExpr._new(qNameCls);
 118         inv.arg("namespace");
 119         inv.arg("localpart");
 120 
 121         if (options.useBaseResourceAndURLToLoadWSDL) {
 122             writeClassLoaderBaseResourceWSDLLocation(className, cls, urlField, exField);
 123         } else if (wsdlLocation.startsWith("http://") || wsdlLocation.startsWith("https://") || wsdlLocation.startsWith("file:/")) {
 124             writeAbsWSDLLocation(cls, urlField, exField);
 125         } else if (wsdlLocation.startsWith("META-INF/")) {
 126             writeClassLoaderResourceWSDLLocation(className, cls, urlField, exField);
 127         } else {
 128             writeResourceWSDLLocation(className, cls, urlField, exField);
 129         }
 130 
 131         //write class comment - JAXWS warning
 132         JDocComment comment = cls.javadoc();
 133 
 134         if (service.getJavaDoc() != null) {
 135             comment.add(service.getJavaDoc());
 136             comment.add("\n\n");
 137         }
 138 
 139         for (String doc : getJAXWSClassComment()) {
 140             comment.add(doc);
 141         }
 142 
 143         // Generating constructor
 144         // for e.g:  public ExampleService()
 145         JMethod constructor1 = cls.constructor(JMod.PUBLIC);
 146         String constructor1Str = String.format("super(__getWsdlLocation(), %s);", serviceName);
 147         constructor1.body().directStatement(constructor1Str);
 148 
 149         // Generating constructor
 150         // for e.g:  public ExampleService(WebServiceFeature ... features)
 151         if (options.target.isLaterThan(Options.Target.V2_2)) {
 152             JMethod constructor2 = cls.constructor(JMod.PUBLIC);
 153             constructor2.varParam(WebServiceFeature.class, "features");
 154             String constructor2Str = String.format("super(__getWsdlLocation(), %s, features);", serviceName);
 155             constructor2.body().directStatement(constructor2Str);
 156         }
 157 
 158         // Generating constructor
 159         // for e.g:  public ExampleService(URL wsdlLocation)
 160         if (options.target.isLaterThan(Options.Target.V2_2)) {
 161             JMethod constructor3 = cls.constructor(JMod.PUBLIC);
 162             constructor3.param(URL.class, "wsdlLocation");
 163             String constructor3Str = String.format("super(wsdlLocation, %s);", serviceName);
 164             constructor3.body().directStatement(constructor3Str);
 165         }
 166 
 167         // Generating constructor
 168         // for e.g:  public ExampleService(URL wsdlLocation, WebServiceFeature ... features)
 169         if (options.target.isLaterThan(Options.Target.V2_2)) {
 170             JMethod constructor4 = cls.constructor(JMod.PUBLIC);
 171             constructor4.param(URL.class, "wsdlLocation");
 172             constructor4.varParam(WebServiceFeature.class, "features");
 173             String constructor4Str = String.format("super(wsdlLocation, %s, features);", serviceName);
 174             constructor4.body().directStatement(constructor4Str);
 175         }
 176 
 177         // Generating constructor
 178         // for e.g:  public ExampleService(URL wsdlLocation, QName serviceName)
 179         JMethod constructor5 = cls.constructor(JMod.PUBLIC);
 180         constructor5.param(URL.class, "wsdlLocation");
 181         constructor5.param(QName.class, "serviceName");
 182         constructor5.body().directStatement("super(wsdlLocation, serviceName);");
 183 
 184         // Generating constructor
 185         // for e.g:  public ExampleService(URL, QName, WebServiceFeature ...)
 186         if (options.target.isLaterThan(Options.Target.V2_2)) {
 187             JMethod constructor6 = cls.constructor(JMod.PUBLIC);
 188             constructor6.param(URL.class, "wsdlLocation");
 189             constructor6.param(QName.class, "serviceName");
 190             constructor6.varParam(WebServiceFeature.class, "features");
 191             constructor6.body().directStatement("super(wsdlLocation, serviceName, features);");
 192         }
 193 
 194         //@WebService
 195         JAnnotationUse webServiceClientAnn = cls.annotate(cm.ref(WebServiceClient.class));
 196         writeWebServiceClientAnnotation(service, webServiceClientAnn);
 197 
 198         // additional annotations
 199         for (GeneratorExtension f:ServiceFinder.find(GeneratorExtension.class)) {
 200             f.writeWebServiceClientAnnotation(options, cm, cls);
 201         }
 202 
 203 
 204         //@HandlerChain
 205         writeHandlerConfig(Names.customJavaTypeClassName(service.getJavaInterface()), cls, options);
 206 
 207         for (Port port : service.getPorts()) {
 208             if (port.isProvider()) {
 209                 continue;  // No getXYZPort() for porvider based endpoint
 210             }
 211 
 212             //Get the SEI class
 213             JType retType;
 214             try {
 215                 retType = getClass(port.getJavaInterface().getName(), ClassType.INTERFACE);
 216             } catch (JClassAlreadyExistsException e) {
 217                 QName portTypeName =
 218                         (QName) port.getProperty(
 219                                 ModelProperties.PROPERTY_WSDL_PORT_TYPE_NAME);
 220                 Locator loc = null;
 221                 if (portTypeName != null) {
 222                     PortType pt = port.portTypes.get(portTypeName);
 223                     if (pt != null) {
 224                         loc = pt.getLocator();
 225                     }
 226                 }
 227                 receiver.error(loc, GeneratorMessages.GENERATOR_SEI_CLASS_ALREADY_EXIST(port.getJavaInterface().getName(), portTypeName));
 228                 return;
 229             }
 230 
 231             //write getXyzPort()
 232             writeDefaultGetPort(port, retType, cls);
 233 
 234             //write getXyzPort(WebServicesFeature...)
 235             if (options.target.isLaterThan(Options.Target.V2_1)) {
 236                 writeGetPort(port, retType, cls);
 237             }
 238         }
 239 
 240         writeGetWsdlLocation(cm.ref(URL.class), cls, urlField, exField);
 241     }
 242 
 243     private void writeGetPort(Port port, JType retType, JDefinedClass cls) {
 244         JMethod m = cls.method(JMod.PUBLIC, retType, port.getPortGetter());
 245         JDocComment methodDoc = m.javadoc();
 246         if (port.getJavaDoc() != null) {
 247             methodDoc.add(port.getJavaDoc());
 248         }
 249         JCommentPart ret = methodDoc.addReturn();
 250         JCommentPart paramDoc = methodDoc.addParam("features");
 251         paramDoc.append("A list of ");
 252         paramDoc.append("{@link " + WebServiceFeature.class.getName() + "}");
 253         paramDoc.append("to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.");
 254         ret.add("returns " + retType.name());
 255         m.varParam(WebServiceFeature.class, "features");
 256         JBlock body = m.body();
 257         StringBuilder statement = new StringBuilder("return ");
 258         statement.append("super.getPort(new QName(\"").append(port.getName().getNamespaceURI()).append("\", \"").append(port.getName().getLocalPart()).append("\"), ");
 259         statement.append(retType.name());
 260         statement.append(".class, features);");
 261         body.directStatement(statement.toString());
 262         writeWebEndpoint(port, m);
 263     }
 264 
 265 
 266     /*
 267        Generates the code to create URL for absolute WSDL location
 268 
 269        for e.g.:
 270        static {
 271            URL url = null;
 272            WebServiceException e = null;
 273            try {
 274                 url = new URL("http://ExampleService.wsdl");
 275            } catch (MalformedURLException ex) {
 276                 e = new WebServiceException(ex);
 277            }
 278            EXAMPLESERVICE_WSDL_LOCATION = url;
 279            EXAMPLESERVICE_EXCEPTION = e;
 280        }
 281     */
 282     private void writeAbsWSDLLocation(JDefinedClass cls, JFieldVar urlField, JFieldVar exField) {
 283         JBlock staticBlock = cls.init();
 284         JVar urlVar = staticBlock.decl(cm.ref(URL.class), "url", JExpr._null());
 285         JVar exVar = staticBlock.decl(cm.ref(WebServiceException.class), "e", JExpr._null());
 286 
 287         JTryBlock tryBlock = staticBlock._try();
 288         tryBlock.body().assign(urlVar, JExpr._new(cm.ref(URL.class)).arg(wsdlLocation));
 289         JCatchBlock catchBlock = tryBlock._catch(cm.ref(MalformedURLException.class));
 290         catchBlock.param("ex");
 291         catchBlock.body().assign(exVar, JExpr._new(cm.ref(WebServiceException.class)).arg(JExpr.ref("ex")));
 292 
 293         staticBlock.assign(urlField, urlVar);
 294         staticBlock.assign(exField, exVar);
 295     }
 296 
 297     /*
 298        Generates the code to create URL for WSDL location as resource
 299 
 300        for e.g.:
 301        static {
 302            EXAMPLESERVICE_WSDL_LOCATION = ExampleService.class.getResource(...);
 303            Exception e = null;
 304            if (EXAMPLESERVICE_WSDL_LOCATION == null) {
 305                e = new WebServiceException("...");
 306            }
 307            EXAMPLESERVICE_EXCEPTION = e;
 308        }
 309      */
 310     private void writeResourceWSDLLocation(String className, JDefinedClass cls, JFieldVar urlField, JFieldVar exField) {
 311         JBlock staticBlock = cls.init();
 312         staticBlock.assign(urlField, JExpr.dotclass(cm.ref(className)).invoke("getResource").arg(wsdlLocation));
 313         JVar exVar = staticBlock.decl(cm.ref(WebServiceException.class), "e", JExpr._null());
 314         JConditional ifBlock = staticBlock._if(urlField.eq(JExpr._null()));
 315         ifBlock._then().assign(exVar, JExpr._new(cm.ref(WebServiceException.class)).arg(
 316                 "Cannot find "+JExpr.quotify('\'', wsdlLocation)+" wsdl. Place the resource correctly in the classpath."));
 317         staticBlock.assign(exField, exVar);
 318     }
 319 
 320     /*
 321        Generates the code to create URL for WSDL location as classloader resource
 322 
 323        for e.g.:
 324        static {
 325            EXAMPLESERVICE_WSDL_LOCATION = ExampleService.class.getClassLoader().getResource(...);
 326            Exception e = null;
 327            if (EXAMPLESERVICE_WSDL_LOCATION == null) {
 328                e = new WebServiceException("...");
 329            }
 330            EXAMPLESERVICE_EXCEPTION = e;
 331        }
 332      */
 333     private void writeClassLoaderResourceWSDLLocation(String className, JDefinedClass cls, JFieldVar urlField, JFieldVar exField) {
 334         JBlock staticBlock = cls.init();
 335         staticBlock.assign(urlField, JExpr.dotclass(cm.ref(className)).invoke("getClassLoader").invoke("getResource").arg(wsdlLocation));
 336         JVar exVar = staticBlock.decl(cm.ref(WebServiceException.class), "e", JExpr._null());
 337         JConditional ifBlock = staticBlock._if(urlField.eq(JExpr._null()));
 338         ifBlock._then().assign(exVar, JExpr._new(cm.ref(WebServiceException.class)).arg(
 339                 "Cannot find "+JExpr.quotify('\'', wsdlLocation)+" wsdl. Place the resource correctly in the classpath."));
 340         staticBlock.assign(exField, exVar);
 341     }
 342 
 343     /*
 344        Generates the code to create URL for WSDL location from classloader base resource
 345 
 346        for e.g.:
 347        static {
 348            Exception e = null;
 349            URL url = null;
 350            try {
 351                url = new URL(ExampleService.class.getClassLoader().getResource("."), ...);
 352            } catch (MalformedURLException murl) {
 353                e = new WebServiceException(murl);
 354            }
 355            EXAMPLESERVICE_WSDL_LOCATION = url;
 356            EXAMPLESERVICE_EXCEPTION = e;
 357        }
 358      */
 359      private void writeClassLoaderBaseResourceWSDLLocation(String className, JDefinedClass cls, JFieldVar urlField, JFieldVar exField) {
 360          JBlock staticBlock = cls.init();
 361          JVar exVar = staticBlock.decl(cm.ref(WebServiceException.class), "e", JExpr._null());
 362          JVar urlVar = staticBlock.decl(cm.ref(URL.class), "url", JExpr._null());
 363          JTryBlock tryBlock = staticBlock._try();
 364          tryBlock.body().assign(urlVar, JExpr._new(cm.ref(URL.class)).arg(JExpr.dotclass(cm.ref(className)).invoke("getResource").arg(".")).arg(wsdlLocation));
 365          JCatchBlock catchBlock = tryBlock._catch(cm.ref(MalformedURLException.class));
 366          JVar murlVar = catchBlock.param("murl");
 367          catchBlock.body().assign(exVar, JExpr._new(cm.ref(WebServiceException.class)).arg(murlVar));
 368          staticBlock.assign(urlField, urlVar);
 369          staticBlock.assign(exField, exVar);
 370      }
 371 
 372     /*
 373        Generates code that gives wsdl URL. If there is an exception in
 374        creating the URL, it throws an exception.
 375 
 376        for example:
 377 
 378        private URL __getWsdlLocation() {
 379            if (EXAMPLESERVICE_EXCEPTION != null) {
 380                throw EXAMPLESERVICE_EXCEPTION;
 381            }
 382            return EXAMPLESERVICE_WSDL_LOCATION;
 383        }
 384      */
 385     private void writeGetWsdlLocation(JType retType, JDefinedClass cls, JFieldVar urlField, JFieldVar exField) {
 386         JMethod m = cls.method(JMod.PRIVATE|JMod.STATIC , retType, "__getWsdlLocation");
 387         JConditional ifBlock = m.body()._if(exField.ne(JExpr._null()));
 388         ifBlock._then()._throw(exField);
 389         m.body()._return(urlField);
 390     }
 391 
 392     private void writeDefaultGetPort(Port port, JType retType, JDefinedClass cls) {
 393         String portGetter = port.getPortGetter();
 394         JMethod m = cls.method(JMod.PUBLIC, retType, portGetter);
 395         JDocComment methodDoc = m.javadoc();
 396         if (port.getJavaDoc() != null) {
 397             methodDoc.add(port.getJavaDoc());
 398         }
 399         JCommentPart ret = methodDoc.addReturn();
 400         ret.add("returns " + retType.name());
 401         JBlock body = m.body();
 402         StringBuilder statement = new StringBuilder("return ");
 403         statement.append("super.getPort(new QName(\"").append(port.getName().getNamespaceURI()).append("\", \"").append(port.getName().getLocalPart()).append("\"), ");
 404         statement.append(retType.name());
 405         statement.append(".class);");
 406         body.directStatement(statement.toString());
 407         writeWebEndpoint(port, m);
 408     }
 409 
 410     private void writeWebServiceClientAnnotation(Service service, JAnnotationUse wsa) {
 411         String serviceName = service.getName().getLocalPart();
 412         String serviceNS = service.getName().getNamespaceURI();
 413         wsa.param("name", serviceName);
 414         wsa.param("targetNamespace", serviceNS);
 415         wsa.param("wsdlLocation", wsdlLocation);
 416     }
 417 
 418     private void writeWebEndpoint(Port port, JMethod m) {
 419         JAnnotationUse webEndpointAnn = m.annotate(cm.ref(WebEndpoint.class));
 420         webEndpointAnn.param("name", port.getName().getLocalPart());
 421     }
 422 }