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.*;
29import com.sun.tools.internal.ws.api.TJavaGeneratorExtension;
30import com.sun.tools.internal.ws.processor.model.*;
31import com.sun.tools.internal.ws.processor.model.java.JavaInterface;
32import com.sun.tools.internal.ws.processor.model.java.JavaMethod;
33import com.sun.tools.internal.ws.processor.model.java.JavaParameter;
34import com.sun.tools.internal.ws.processor.model.jaxb.JAXBType;
35import com.sun.tools.internal.ws.processor.model.jaxb.JAXBTypeAndAnnotation;
36import com.sun.tools.internal.ws.wscompile.ErrorReceiver;
37import com.sun.tools.internal.ws.wscompile.Options;
38import com.sun.tools.internal.ws.wscompile.WsimportOptions;
39import com.sun.tools.internal.ws.wsdl.document.soap.SOAPStyle;
40import com.sun.tools.internal.ws.wsdl.document.PortType;
41import com.sun.tools.internal.ws.resources.GeneratorMessages;
42
43import javax.jws.WebMethod;
44import javax.jws.WebParam;
45import javax.jws.WebService;
46import javax.jws.soap.SOAPBinding;
47import javax.xml.bind.annotation.XmlSeeAlso;
48import javax.xml.namespace.QName;
49import javax.xml.ws.Holder;
50import java.util.ArrayList;
51import java.util.List;
52
53import org.xml.sax.Locator;
54
55public class SeiGenerator extends GeneratorBase {
56    private TJavaGeneratorExtension extension;
57    private List<TJavaGeneratorExtension> extensionHandlers;
58
59    public static void generate(Model model, WsimportOptions options, ErrorReceiver receiver, TJavaGeneratorExtension... extensions){
60        SeiGenerator seiGenerator = new SeiGenerator();
61        seiGenerator.init(model, options, receiver, extensions);
62        seiGenerator.doGeneration();
63    }
64
65    public void init(Model model, WsimportOptions options, ErrorReceiver receiver, TJavaGeneratorExtension... extensions) {
66        init(model, options, receiver);
67        extensionHandlers = new ArrayList<TJavaGeneratorExtension>();
68
69        // register handlers for default extensions
70
71        // 2.2 Spec requires generation of @Action when wsam:Action is explicitly stated in wsdl
72        if (options.target.isLaterThan(Options.Target.V2_2)) {
73           register(new W3CAddressingJavaGeneratorExtension());
74        }
75
76        for (TJavaGeneratorExtension j : extensions) {
77            register(j);
78        }
79
80        this.extension = new JavaGeneratorExtensionFacade(extensionHandlers.toArray(new TJavaGeneratorExtension[extensionHandlers.size()]));
81    }
82
83    private void write(Port port) {
84        JavaInterface intf = port.getJavaInterface();
85        String className = Names.customJavaTypeClassName(intf);
86
87        if (donotOverride && GeneratorUtil.classExists(options, className)) {
88            log("Class " + className + " exists. Not overriding.");
89            return;
90        }
91
92
93        JDefinedClass cls;
94        try {
95            cls = getClass(className, ClassType.INTERFACE);
96        } catch (JClassAlreadyExistsException e) {
97            QName portTypeName =
98                (QName) port.getProperty(
99                        ModelProperties.PROPERTY_WSDL_PORT_TYPE_NAME);
100            Locator loc = null;
101            if(portTypeName != null){
102                PortType pt = port.portTypes.get(portTypeName);
103                if (pt!=null) {
104                    loc = pt.getLocator();
105                }
106            }
107            receiver.error(loc, GeneratorMessages.GENERATOR_SEI_CLASS_ALREADY_EXIST(intf.getName(), portTypeName));
108            return;
109        }
110        // If the class has methods it has already been defined
111        // so skip it.
112        if (!cls.methods().isEmpty()) {
113            return;
114        }
115
116        //write class comment - JAXWS warning
117        JDocComment comment = cls.javadoc();
118
119        String ptDoc = intf.getJavaDoc();
120        if(ptDoc != null){
121            comment.add(ptDoc);
122            comment.add("\n\n");
123        }
124
125        for(String doc:getJAXWSClassComment()){
126                comment.add(doc);
127        }
128
129
130        //@WebService
131        JAnnotationUse webServiceAnn = cls.annotate(cm.ref(WebService.class));
132        writeWebServiceAnnotation(port, webServiceAnn);
133
134        //@HandlerChain
135        writeHandlerConfig(Names.customJavaTypeClassName(port.getJavaInterface()), cls, options);
136
137        //@SOAPBinding
138        writeSOAPBinding(port, cls);
139
140        //@XmlSeeAlso
141        if (options.target.isLaterThan(Options.Target.V2_1)) {
142            writeXmlSeeAlso(cls);
143        }
144
145        for (Operation operation: port.getOperations()) {
146            JavaMethod method = operation.getJavaMethod();
147
148            //@WebMethod
149            JMethod m;
150            JDocComment methodDoc;
151            String methodJavaDoc = operation.getJavaDoc();
152            if(method.getReturnType().getName().equals("void")){
153                m = cls.method(JMod.PUBLIC, void.class, method.getName());
154                methodDoc = m.javadoc();
155            }else {
156                JAXBTypeAndAnnotation retType = method.getReturnType().getType();
157                m = cls.method(JMod.PUBLIC, retType.getType(), method.getName());
158                retType.annotate(m);
159                methodDoc = m.javadoc();
160                JCommentPart ret = methodDoc.addReturn();
161                ret.add("returns "+retType.getName());
162            }
163            if (methodJavaDoc != null) {
164                methodDoc.add(methodJavaDoc);
165            }
166
167            writeWebMethod(operation, m);
168            JClass holder = cm.ref(Holder.class);
169            for (JavaParameter parameter: method.getParametersList()) {
170                JVar var;
171                JAXBTypeAndAnnotation paramType = parameter.getType().getType();
172                if (parameter.isHolder()) {
173                    var = m.param(holder.narrow(paramType.getType().boxify()), parameter.getName());
174                }else{
175                    var = m.param(paramType.getType(), parameter.getName());
176                }
177
178                //annotate parameter with JAXB annotations
179                paramType.annotate(var);
180                methodDoc.addParam(var);
181                JAnnotationUse paramAnn = var.annotate(cm.ref(WebParam.class));
182                writeWebParam(operation, parameter, paramAnn);
183            }
184            com.sun.tools.internal.ws.wsdl.document.Operation wsdlOp = operation.getWSDLPortTypeOperation();
185            for(Fault fault:operation.getFaultsSet()){
186                m._throws(fault.getExceptionClass());
187                methodDoc.addThrows(fault.getExceptionClass());
188                wsdlOp.putFault(fault.getWsdlFaultName(), fault.getExceptionClass());
189            }
190
191            //It should be the last thing to invoke after JMethod is built completely
192            extension.writeMethodAnnotations(wsdlOp, m);
193        }
194    }
195
196    private void writeXmlSeeAlso(JDefinedClass cls) {
197        if (model.getJAXBModel().getS2JJAXBModel() != null) {
198            List<JClass> objectFactories = model.getJAXBModel().getS2JJAXBModel().getAllObjectFactories();
199
200            //if there are no object facotires, dont generate @XmlSeeAlso
201            if (objectFactories.isEmpty()) {
202                return;
203            }
204
205            JAnnotationUse xmlSeeAlso = cls.annotate(cm.ref(XmlSeeAlso.class));
206            JAnnotationArrayMember paramArray = xmlSeeAlso.paramArray("value");
207            for (JClass of : objectFactories) {
208                paramArray = paramArray.param(of);
209            }
210        }
211
212    }
213
214    private void writeWebMethod(Operation operation, JMethod m) {
215        Response response = operation.getResponse();
216        JAnnotationUse webMethodAnn = m.annotate(cm.ref(WebMethod.class));
217        String operationName = (operation instanceof AsyncOperation)?
218                ((AsyncOperation)operation).getNormalOperation().getName().getLocalPart():
219                operation.getName().getLocalPart();
220
221        if(!m.name().equals(operationName)){
222            webMethodAnn.param("operationName", operationName);
223        }
224
225        if (operation.getSOAPAction() != null && operation.getSOAPAction().length() > 0){
226            webMethodAnn.param("action", operation.getSOAPAction());
227        }
228
229        if (operation.getResponse() == null){
230            m.annotate(javax.jws.Oneway.class);
231        }else if (!operation.getJavaMethod().getReturnType().getName().equals("void") &&
232                 operation.getResponse().getParametersList().size() > 0){
233            Block block;
234            String resultName = null;
235            String nsURI = null;
236            if (operation.getResponse().getBodyBlocks().hasNext()) {
237                block = operation.getResponse().getBodyBlocks().next();
238                resultName = block.getName().getLocalPart();
239                if(isDocStyle || block.getLocation() == Block.HEADER){
240                    nsURI = block.getName().getNamespaceURI();
241                }
242            }
243
244            for (Parameter parameter : operation.getResponse().getParametersList()) {
245                if (parameter.getParameterIndex() == -1) {
246                    if(operation.isWrapped()||!isDocStyle){
247                        if(parameter.getBlock().getLocation() == Block.HEADER){
248                            resultName = parameter.getBlock().getName().getLocalPart();
249                        }else{
250                            resultName = parameter.getName();
251                        }
252                        if (isDocStyle || (parameter.getBlock().getLocation() == Block.HEADER)) {
253                            nsURI = parameter.getType().getName().getNamespaceURI();
254                        }
255                    }else if(isDocStyle){
256                        JAXBType t = (JAXBType)parameter.getType();
257                        resultName = t.getName().getLocalPart();
258                        nsURI = t.getName().getNamespaceURI();
259                    }
260                    if(!(operation instanceof AsyncOperation)){
261                        JAnnotationUse wr = null;
262
263                        if(!resultName.equals("return")){
264                            wr = m.annotate(javax.jws.WebResult.class);
265                            wr.param("name", resultName);
266                        }
267                        if (nsURI != null || (isDocStyle && operation.isWrapped())) {
268                            if(wr == null) {
269                                wr = m.annotate(javax.jws.WebResult.class);
270                            }
271                            wr.param("targetNamespace", nsURI);
272                        }
273                        //doclit wrapped could have additional headers
274                        if(!(isDocStyle && operation.isWrapped()) ||
275                                (parameter.getBlock().getLocation() == Block.HEADER)){
276                            if (wr == null) {
277                                wr = m.annotate(javax.jws.WebResult.class);
278                            }
279                            wr.param("partName", parameter.getName());
280                        }
281                        if(parameter.getBlock().getLocation() == Block.HEADER){
282                            if (wr == null) {
283                                wr = m.annotate(javax.jws.WebResult.class);
284                            }
285                            wr.param("header",true);
286                        }
287                    }
288                }
289
290            }
291        }
292
293        //DOC/BARE
294        if (!sameParamStyle) {
295            if(!operation.isWrapped()) {
296               JAnnotationUse sb = m.annotate(SOAPBinding.class);
297               sb.param("parameterStyle", SOAPBinding.ParameterStyle.BARE);
298            }
299        }
300
301        if (operation.isWrapped() && operation.getStyle().equals(SOAPStyle.DOCUMENT)) {
302            Block reqBlock = operation.getRequest().getBodyBlocks().next();
303            JAnnotationUse reqW = m.annotate(javax.xml.ws.RequestWrapper.class);
304            reqW.param("localName", reqBlock.getName().getLocalPart());
305            reqW.param("targetNamespace", reqBlock.getName().getNamespaceURI());
306            reqW.param("className", reqBlock.getType().getJavaType().getName());
307
308            if (response != null) {
309                JAnnotationUse resW = m.annotate(javax.xml.ws.ResponseWrapper.class);
310                Block resBlock = response.getBodyBlocks().next();
311                resW.param("localName", resBlock.getName().getLocalPart());
312                resW.param("targetNamespace", resBlock.getName().getNamespaceURI());
313                resW.param("className", resBlock.getType().getJavaType().getName());
314            }
315        }
316    }
317
318    private boolean isMessageParam(Parameter param, Message message) {
319        Block block = param.getBlock();
320
321        return (message.getBodyBlockCount() > 0 && block.equals(message.getBodyBlocks().next())) ||
322               (message.getHeaderBlockCount() > 0 &&
323               block.equals(message.getHeaderBlocks().next()));
324    }
325
326    private boolean isHeaderParam(Parameter param, Message message) {
327        if (message.getHeaderBlockCount() == 0) {
328            return false;
329        }
330
331        for (Block headerBlock : message.getHeaderBlocksMap().values()) {
332            if (param.getBlock().equals(headerBlock)) {
333                return true;
334            }
335        }
336
337        return false;
338    }
339
340    private boolean isAttachmentParam(Parameter param, Message message){
341        if (message.getAttachmentBlockCount() == 0) {
342            return false;
343        }
344
345        for (Block attBlock : message.getAttachmentBlocksMap().values()) {
346            if (param.getBlock().equals(attBlock)) {
347                return true;
348            }
349        }
350
351        return false;
352    }
353
354    private boolean isUnboundParam(Parameter param, Message message){
355        if (message.getUnboundBlocksCount() == 0) {
356            return false;
357        }
358
359        for (Block unboundBlock : message.getUnboundBlocksMap().values()) {
360            if (param.getBlock().equals(unboundBlock)) {
361                return true;
362            }
363        }
364
365        return false;
366    }
367
368    private void writeWebParam(Operation operation, JavaParameter javaParameter, JAnnotationUse paramAnno) {
369        Parameter param = javaParameter.getParameter();
370        Request req = operation.getRequest();
371        Response res = operation.getResponse();
372
373        boolean header = isHeaderParam(param, req) ||
374            (res != null && isHeaderParam(param, res));
375
376        String name;
377        boolean isWrapped = operation.isWrapped();
378
379        if ((param.getBlock().getLocation() == Block.HEADER) || (isDocStyle && !isWrapped)) {
380            name = param.getBlock().getName().getLocalPart();
381        } else {
382            name = param.getName();
383        }
384
385        paramAnno.param("name", name);
386
387        String ns= null;
388
389        if (isDocStyle) {
390            ns = param.getBlock().getName().getNamespaceURI(); // its bare nsuri
391            if(isWrapped){
392                ns = param.getType().getName().getNamespaceURI();
393            }
394        }else if(header){
395            ns = param.getBlock().getName().getNamespaceURI();
396        }
397
398        if (ns != null || (isDocStyle && isWrapped)) {
399            paramAnno.param("targetNamespace", ns);
400        }
401
402        if (header) {
403            paramAnno.param("header", true);
404        }
405
406        if (param.isINOUT()){
407            paramAnno.param("mode", javax.jws.WebParam.Mode.INOUT);
408        }else if ((res != null) && (isMessageParam(param, res) || isHeaderParam(param, res) || isAttachmentParam(param, res) ||
409                isUnboundParam(param,res) || param.isOUT())){
410            paramAnno.param("mode", javax.jws.WebParam.Mode.OUT);
411        }
412
413        //doclit wrapped could have additional headers
414        if (!(isDocStyle && isWrapped) || header) {
415            paramAnno.param("partName", javaParameter.getParameter().getName());
416        }
417    }
418
419    private boolean isDocStyle = true;
420    private boolean sameParamStyle = true;
421    private void writeSOAPBinding(Port port, JDefinedClass cls) {
422        JAnnotationUse soapBindingAnn = null;
423        isDocStyle = port.getStyle() == null || port.getStyle().equals(SOAPStyle.DOCUMENT);
424        if(!isDocStyle){
425            soapBindingAnn = cls.annotate(SOAPBinding.class);
426            soapBindingAnn.param("style", SOAPBinding.Style.RPC);
427            port.setWrapped(true);
428        }
429        if(isDocStyle){
430            boolean first = true;
431            boolean isWrapper = true;
432            for(Operation operation:port.getOperations()){
433                if(first){
434                    isWrapper = operation.isWrapped();
435                    first = false;
436                    continue;
437                }
438                sameParamStyle = (isWrapper == operation.isWrapped());
439                if (!sameParamStyle) {
440                    break;
441                }
442            }
443            if (sameParamStyle) {
444                port.setWrapped(isWrapper);
445            }
446        }
447        if(sameParamStyle && !port.isWrapped()){
448            if (soapBindingAnn == null) {
449                soapBindingAnn = cls.annotate(SOAPBinding.class);
450            }
451            soapBindingAnn.param("parameterStyle", SOAPBinding.ParameterStyle.BARE);
452        }
453    }
454
455    private void writeWebServiceAnnotation(Port port, JAnnotationUse wsa) {
456        QName name = (QName) port.getProperty(ModelProperties.PROPERTY_WSDL_PORT_TYPE_NAME);
457        wsa.param("name", name.getLocalPart());
458        wsa.param("targetNamespace", name.getNamespaceURI());
459    }
460
461    @Override
462    public void visit(Model model) throws Exception {
463        for(Service s:model.getServices()){
464            s.accept(this);
465        }
466    }
467
468    @Override
469    public void visit(Service service) throws Exception {
470        String jd = model.getJavaDoc();
471        if(jd != null){
472            JPackage pkg = cm._package(options.defaultPackage);
473            pkg.javadoc().add(jd);
474        }
475
476        for(Port p:service.getPorts()){
477            visitPort(service, p);
478        }
479    }
480
481    private void visitPort(Service service, Port port) {
482        if (port.isProvider()) {
483            return;                // Not generating for Provider based endpoint
484        }
485        write(port);
486    }
487
488    private void register(TJavaGeneratorExtension h) {
489        extensionHandlers.add(h);
490    }
491}
492