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
26package com.sun.tools.internal.ws.processor.generator;
27
28import com.sun.codemodel.internal.ClassType;
29import com.sun.codemodel.internal.JAnnotationUse;
30import com.sun.codemodel.internal.JBlock;
31import com.sun.codemodel.internal.JCatchBlock;
32import com.sun.codemodel.internal.JClass;
33import com.sun.codemodel.internal.JClassAlreadyExistsException;
34import com.sun.codemodel.internal.JCommentPart;
35import com.sun.codemodel.internal.JConditional;
36import com.sun.codemodel.internal.JDefinedClass;
37import com.sun.codemodel.internal.JDocComment;
38import com.sun.codemodel.internal.JExpr;
39import com.sun.codemodel.internal.JFieldVar;
40import com.sun.codemodel.internal.JInvocation;
41import com.sun.codemodel.internal.JMethod;
42import com.sun.codemodel.internal.JMod;
43import com.sun.codemodel.internal.JTryBlock;
44import com.sun.codemodel.internal.JType;
45import com.sun.codemodel.internal.JVar;
46import com.sun.tools.internal.ws.processor.model.Model;
47import com.sun.tools.internal.ws.processor.model.ModelProperties;
48import com.sun.tools.internal.ws.processor.model.Port;
49import com.sun.tools.internal.ws.processor.model.Service;
50import com.sun.tools.internal.ws.processor.model.java.JavaInterface;
51import com.sun.tools.internal.ws.resources.GeneratorMessages;
52import com.sun.tools.internal.ws.wscompile.ErrorReceiver;
53import com.sun.tools.internal.ws.wscompile.Options;
54import com.sun.tools.internal.ws.wscompile.WsimportOptions;
55import com.sun.tools.internal.ws.wsdl.document.PortType;
56import com.sun.xml.internal.ws.spi.db.BindingHelper;
57
58import org.xml.sax.Locator;
59
60import javax.xml.namespace.QName;
61import javax.xml.ws.WebEndpoint;
62import javax.xml.ws.WebServiceClient;
63import javax.xml.ws.WebServiceFeature;
64import javax.xml.ws.WebServiceException;
65import java.net.MalformedURLException;
66import java.net.URL;
67
68import com.sun.xml.internal.ws.util.ServiceFinder;
69import java.util.Locale;
70
71/**
72 * @author WS Development Team
73 * @author Jitendra Kotamraju
74 */
75public 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}
423