JwsImplGenerator.java revision 524:dcaa586ab756
1217309Snwhitehorn/*
2217309Snwhitehorn * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3217309Snwhitehorn * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4217309Snwhitehorn *
5217309Snwhitehorn * This code is free software; you can redistribute it and/or modify it
6217309Snwhitehorn * under the terms of the GNU General Public License version 2 only, as
7217309Snwhitehorn * published by the Free Software Foundation.  Oracle designates this
8217309Snwhitehorn * particular file as subject to the "Classpath" exception as provided
9217309Snwhitehorn * 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.processor.model.*;
30import com.sun.tools.internal.ws.processor.model.java.JavaInterface;
31import com.sun.tools.internal.ws.processor.model.java.JavaMethod;
32import com.sun.tools.internal.ws.processor.model.java.JavaParameter;
33import com.sun.tools.internal.ws.processor.model.jaxb.JAXBTypeAndAnnotation;
34import com.sun.tools.internal.ws.wsdl.document.Definitions;
35import com.sun.tools.internal.ws.wsdl.document.Binding;
36import com.sun.tools.internal.ws.wsdl.document.soap.SOAP12Binding;
37import com.sun.tools.internal.ws.wsdl.document.soap.SOAPBinding;
38import com.sun.tools.internal.ws.wsdl.document.soap.SOAPConstants;
39import com.sun.tools.internal.ws.api.wsdl.TWSDLExtension;
40import com.sun.tools.internal.ws.wscompile.ErrorReceiver;
41import com.sun.tools.internal.ws.processor.model.ModelProperties;
42import com.sun.tools.internal.ws.wscompile.WsimportOptions;
43import com.sun.codemodel.internal.JClassAlreadyExistsException;
44import com.sun.xml.internal.ws.api.SOAPVersion;
45
46import com.sun.xml.internal.ws.util.ServiceFinder;
47
48import javax.jws.WebService;
49import javax.xml.ws.BindingType;
50import javax.xml.namespace.QName;
51import javax.xml.ws.Holder;
52import java.io.File;
53import java.text.MessageFormat;
54import java.util.ArrayList;
55import java.util.HashMap;
56import java.util.List;
57import java.util.Iterator;
58import java.util.Map;
59
60/**
61 * Generator for placeholder JWS implementations
62 *
63 * @since 2.2.6
64 */
65public final class JwsImplGenerator extends GeneratorBase {
66        private static final Map<String, String> TRANSLATION_MAP = new HashMap<String, String>(
67      1);
68        static
69  {
70    TRANSLATION_MAP.put(SOAPConstants.URI_SOAP_TRANSPORT_HTTP,
71                javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING);
72  }
73        // save the generated impl files' info
74        private final List<String> implFiles = new ArrayList<String>();
75
76        public static List<String> generate(Model model, WsimportOptions options,
77            ErrorReceiver receiver) {
78                // options check
79
80                // Generate it according the implDestDir option
81                if (options.implDestDir == null)
82                        return null;
83
84                JwsImplGenerator jwsImplGenerator = new JwsImplGenerator();
85                jwsImplGenerator.init(model, options, receiver);
86                jwsImplGenerator.doGeneration();
87                // print a warning message while implFiles.size() is zero
88                if (jwsImplGenerator.implFiles.isEmpty()) {
89                        StringBuilder msg = new StringBuilder();
90                        if (options.implServiceName != null)
91                                msg.append("serviceName=[").append(options.implServiceName).append("] ");
92                        if (options.implPortName != null)
93                                msg.append("portName=[").append(options.implPortName).append("] ");
94
95                        if (msg.length() > 0)
96                                msg.append(", Not found in wsdl file.\n");
97
98                        msg.append("No impl files generated!");
99                        receiver.warning(null, msg.toString());
100                }
101
102                return jwsImplGenerator.implFiles;
103        }
104
105        /**
106         * Move impl files to implDestDir
107         */
108        public static boolean moveToImplDestDir(List<String> gImplFiles,
109            WsimportOptions options, ErrorReceiver receiver) {
110                if (options.implDestDir == null || gImplFiles == null
111                    || gImplFiles.isEmpty())
112                        return true;
113
114                List<ImplFile> generatedImplFiles = ImplFile.toImplFiles(gImplFiles);
115
116                try {
117                        File implDestDir = makePackageDir(options);
118
119                        File movedF;
120                        File f;
121                        for (ImplFile implF : generatedImplFiles) {
122                                movedF = findFile(options, implF.qualifiedName);
123                                if (movedF == null) {
124                                        // should never happen
125                                        receiver.warning(null, "Class " + implF.qualifiedName
126                                            + " is not generated. Not moving.");
127                                        return false;
128                                }
129
130                                f = new File(implDestDir, implF.name);
131                            if (!movedF.equals(f)) {    //bug 10102169
132
133                    if (f.exists())
134                    {
135                        if (!f.delete()){
136                            receiver.error("Class " + implF.qualifiedName
137                                    + " has existed in destImplDir, and it "
138                                    + "can not be written!", null);
139                        }
140                    }
141                    if(!movedF.renameTo(f))
142                    {
143                        throw new Exception();
144                    }
145                }
146                        }
147                } catch (Exception e) {
148                        receiver.error("Moving WebService Impl files failed!", e);
149                        return false;
150                }
151                return true;
152        }
153
154        private JwsImplGenerator() {
155                donotOverride = true;
156        }
157
158        @Override
159        public void visit(Service service) {
160                QName serviceName = service.getName();
161                // process the ordered service only if it is defined
162                if (options.implServiceName != null
163                    && !equalsNSOptional(options.implServiceName, serviceName))
164                        return;
165
166                for (Port port : service.getPorts()) {
167                        if (port.isProvider()) {
168                                continue; // Not generating for Provider based endpoint
169                        }
170
171                        // Generate the impl class name according to
172                        // Xpath(/definitions/service/port[@name]);
173                        QName portName = port.getName();
174
175                        // process the ordered port only if it is defined
176                        if (options.implPortName != null
177                            && !equalsNSOptional(options.implPortName, portName))
178                                continue;
179
180                        String simpleClassName = serviceName.getLocalPart() + "_"
181                            + portName.getLocalPart() + "Impl";
182                        String className = makePackageQualified(simpleClassName);
183                        implFiles.add(className);
184
185                        if (donotOverride && GeneratorUtil.classExists(options, className)) {
186                                log("Class " + className + " exists. Not overriding.");
187                                return;
188                        }
189
190                        JDefinedClass cls = null;
191                        try {
192                                cls = getClass(className, ClassType.CLASS);
193                        } catch (JClassAlreadyExistsException e) {
194                                log("Class " + className
195                                    + " generates failed. JClassAlreadyExistsException[" + className
196                                    + "].");
197                                return;
198                        }
199
200                        // Each serviceImpl will implements one port interface
201                        JavaInterface portIntf = port.getJavaInterface();
202                        String portClassName = Names.customJavaTypeClassName(portIntf);
203                        JDefinedClass portCls = null;
204                        try {
205                                portCls = getClass(portClassName, ClassType.INTERFACE);
206                        } catch (JClassAlreadyExistsException e) {
207                                log("Class " + className
208                                    + " generates failed. JClassAlreadyExistsException["
209                                    + portClassName + "].");
210                                return;
211                        }
212                        cls._implements(portCls);
213
214                        // create a default constructor
215                        cls.constructor(JMod.PUBLIC);
216
217                        // write class comment - JAXWS warning
218                        JDocComment comment = cls.javadoc();
219
220                        if (service.getJavaDoc() != null) {
221                                comment.add(service.getJavaDoc());
222                                comment.add("\n\n");
223                        }
224
225                        for (String doc : getJAXWSClassComment()) {
226                                comment.add(doc);
227                        }
228
229                        // @WebService
230                        JAnnotationUse webServiceAnn = cls.annotate(cm.ref(WebService.class));
231                        writeWebServiceAnnotation(service, port, webServiceAnn);
232
233                        // @BindingType
234                        JAnnotationUse bindingTypeAnn = cls.annotate(cm.ref(BindingType.class));
235                        writeBindingTypeAnnotation(port, bindingTypeAnn);
236
237                        // extra annotation
238                        for( GeneratorExtension f : ServiceFinder.find(GeneratorExtension.class) ) {
239                            f.writeWebServiceAnnotation(model, cm, cls, port);
240                        }
241
242                        // WebMethods
243                        for (Operation operation : port.getOperations()) {
244                                JavaMethod method = operation.getJavaMethod();
245
246                                // @WebMethod
247                                JMethod m;
248                                JDocComment methodDoc;
249                                String methodJavaDoc = operation.getJavaDoc();
250                                if (method.getReturnType().getName().equals("void")) {
251                                        m = cls.method(JMod.PUBLIC, void.class, method.getName());
252                                        methodDoc = m.javadoc();
253                                } else {
254                                        JAXBTypeAndAnnotation retType = method.getReturnType().getType();
255                                        m = cls.method(JMod.PUBLIC, retType.getType(), method.getName());
256                                        retType.annotate(m);
257                                        methodDoc = m.javadoc();
258                                        JCommentPart ret = methodDoc.addReturn();
259                                        ret.add("returns " + retType.getName());
260                                }
261
262                                if (methodJavaDoc != null)
263                                        methodDoc.add(methodJavaDoc);
264
265                                JClass holder = cm.ref(Holder.class);
266                                for (JavaParameter parameter : method.getParametersList()) {
267                                        JVar var;
268                                        JAXBTypeAndAnnotation paramType = parameter.getType().getType();
269                                        if (parameter.isHolder()) {
270                                                var = m.param(holder.narrow(paramType.getType().boxify()),
271                                                    parameter.getName());
272                                        } else {
273                                                var = m.param(paramType.getType(), parameter.getName());
274                                        }
275                                        methodDoc.addParam(var);
276                                }
277
278                                com.sun.tools.internal.ws.wsdl.document.Operation wsdlOp = operation
279                                    .getWSDLPortTypeOperation();
280                                for (Fault fault : operation.getFaultsSet()) {
281                                        m._throws(fault.getExceptionClass());
282                                        methodDoc.addThrows(fault.getExceptionClass());
283                                        wsdlOp.putFault(fault.getWsdlFaultName(), fault.getExceptionClass());
284                                }
285                                m.body().block().directStatement("//replace with your impl here");
286                                m.body().block().directStatement(
287                                    getReturnString(method.getReturnType().getName()));
288                        }
289                }
290        }
291
292        /**
293         * Generate return statement according to return type.
294         *
295         * @param type
296         *          The method's return type
297         * @return The whole return statement
298         */
299        private String getReturnString(String type) {
300                final String nullReturnStr = "return null;";
301                // complex type or array
302                if (type.indexOf('.') > -1 || type.indexOf('[') > -1) {
303                        return nullReturnStr;
304                }
305
306                // primitive type
307                if (type.equals("void")) {
308                        return "return;";
309                }
310                if (type.equals("boolean")) {
311                        return "return false;";
312                }
313                if (type.equals("int") || type.equals("byte") || type.equals("short")
314                    || type.equals("long") || type.equals("double") || type.equals("float")) {
315                        return "return 0;";
316                }
317                if (type.equals("char")) {
318                        return "return '0';";
319                }
320
321                return nullReturnStr;
322        }
323
324        /**
325         *
326         * @param service
327         * @param port
328         * @param webServiceAnn
329         * @param options
330         */
331        private void writeWebServiceAnnotation(Service service, Port port,
332            JAnnotationUse webServiceAnn) {
333                webServiceAnn.param("portName", port.getName().getLocalPart());
334                webServiceAnn.param("serviceName", service.getName().getLocalPart());
335                webServiceAnn.param("targetNamespace", service.getName().getNamespaceURI());
336                webServiceAnn.param("wsdlLocation", wsdlLocation);
337                webServiceAnn.param("endpointInterface", port.getJavaInterface().getName());
338        }
339
340        //CR373098 To transform the java class name as validate.
341        private String transToValidJavaIdentifier(String s) {
342            if (s == null) {
343                return null;
344            }
345            final int len = s.length();
346            StringBuilder retSB = new StringBuilder();
347            if (len == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) {
348                retSB.append("J"); //update to a default start char
349            } else {
350                retSB.append(s.charAt(0));
351            }
352
353            for (int i = 1; i < len; i++) {
354                if (!Character.isJavaIdentifierPart(s.charAt(i)))
355                  ; //delete it if it is illegal //TODO: It might conflict "a-b" vs. "ab"
356                else {
357                    retSB.append(s.charAt(i));
358                }
359            }
360            return retSB.toString();
361        }
362
363        private String makePackageQualified(String s) {
364                s = transToValidJavaIdentifier(s);
365                if (options.defaultPackage != null && !options.defaultPackage.equals("")) {
366                        return options.defaultPackage + "." + s;
367                } else {
368                        return s;
369                }
370        }
371
372
373        /**
374         * TODO
375         *
376         * @param port
377         * @param bindingTypeAnn
378         */
379        private void writeBindingTypeAnnotation(Port port,
380            JAnnotationUse bindingTypeAnn) {
381                QName bName = (QName) port
382                    .getProperty(ModelProperties.PROPERTY_WSDL_BINDING_NAME);
383                if (bName == null)
384                        return;
385
386                String v = getBindingType(bName);
387
388                // TODO: How to decide if it is a mtom?
389                if (v != null) {
390                        // transport = translate(transport);
391                        bindingTypeAnn.param("value", v);
392                }
393
394        }
395
396        private String resolveBindingValue(TWSDLExtension wsdlext) {
397                if (wsdlext.getClass().equals(SOAPBinding.class)) {
398                        SOAPBinding sb = (SOAPBinding) wsdlext;
399                        if(javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING.equals(sb.getTransport()))
400                                return javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING;
401                        else {
402                            for(GeneratorExtension f : ServiceFinder.find(GeneratorExtension.class) ) {
403                                String bindingValue = f.getBindingValue(sb.getTransport(), SOAPVersion.SOAP_11);
404                                if(bindingValue!=null) {
405                                    return bindingValue;
406                                }
407                            }
408                                return javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING;
409                        }
410                }
411                if (wsdlext.getClass().equals(SOAP12Binding.class)) {
412                        SOAP12Binding sb = (SOAP12Binding) wsdlext;
413                        if(javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_MTOM_BINDING.equals(sb.getTransport()))
414                                return javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_MTOM_BINDING;
415                    else {
416                        for(GeneratorExtension f : ServiceFinder.find(GeneratorExtension.class) ) {
417                            String bindingValue = f.getBindingValue(sb.getTransport(), SOAPVersion.SOAP_12);
418                            if(bindingValue!=null) {
419                                return bindingValue;
420                            }
421                        }
422                            return javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING;
423                    }
424                }
425                return null;
426        }
427
428        private String getBindingType(QName bName) {
429
430                String value = null;
431                // process the bindings in definitions of model.entity
432                if (model.getEntity() instanceof Definitions) {
433                        Definitions definitions = (Definitions) model.getEntity();
434                        if (definitions != null) {
435                                Iterator bindings = definitions.bindings();
436                                if (bindings != null) {
437                                        while (bindings.hasNext()) {
438                                                Binding binding = (Binding) bindings.next();
439                                                if (bName.getLocalPart().equals(binding.getName())
440                                                    && bName.getNamespaceURI().equals(binding.getNamespaceURI())) {
441                                                        List<TWSDLExtension> bindextends = (List<TWSDLExtension>) binding
442                                                            .extensions();
443                                                        for (TWSDLExtension wsdlext : bindextends) {
444                                                                value = resolveBindingValue(wsdlext);
445                                                                if (value != null)
446                                                                        break;
447                                                        }
448                                                        break;
449                                                }
450                                        }
451                                }
452                        }
453                }
454
455                // process the bindings in whole document
456                if (value == null) {
457                        if (model.getEntity() instanceof Definitions) {
458                            Definitions definitions = (Definitions) model.getEntity();
459                            Binding b = (Binding) definitions.resolveBindings().get(bName);
460                            if (b != null) {
461                                List<TWSDLExtension> bindextends = (List<TWSDLExtension>) b
462                                    .extensions();
463                                for (TWSDLExtension wsdlext : bindextends) {
464                                    value = resolveBindingValue(wsdlext);
465                                    if (value != null)
466                                    break;
467                                }
468                            }
469                        }
470                }
471
472                return value;
473        }
474
475  /**
476   * Since the SOAP 1.1 binding transport URI defined in WSDL 1.1 specification
477   * is different with the SOAPBinding URI defined by JAX-WS 2.0 specification.
478   * We must translate the wsdl version into JAX-WS version. If the given
479   * transport URI is NOT one of the predefined transport URIs, it is returned
480   * as is.
481   *
482   * @param transportURI
483   *          retrieved from WSDL
484   * @return Standard BindingType URI defined by JAX-WS 2.0 specification.
485   */
486//  private String translate(String transportURI)
487//  {
488//    String translatedBindingId = TRANSLATION_MAP.get(transportURI);
489//    if (translatedBindingId == null)
490//      translatedBindingId = transportURI;
491//
492//    return translatedBindingId;
493//  }
494
495        /*****************************************************************************
496         * Inner classes definition
497         */
498        static final class ImplFile {
499                public String qualifiedName; // package+"."+simpleClassName + ".java"
500
501                public String name; // simpleClassName + ".java"
502
503                private ImplFile(String qualifiedClassName) {
504                        this.qualifiedName = qualifiedClassName + ".java";
505
506                        String simpleClassName = qualifiedClassName;
507                        int i = qualifiedClassName.lastIndexOf(".");
508                        if (i != -1)
509                                simpleClassName = qualifiedClassName.substring(i + 1);
510
511                        this.name = simpleClassName + ".java";
512                }
513
514                public static List<ImplFile> toImplFiles(List<String> qualifiedClassNames) {
515                        List<ImplFile> ret = new ArrayList<ImplFile>();
516
517                        for (String qualifiedClassName : qualifiedClassNames)
518                                ret.add(new ImplFile(qualifiedClassName));
519
520                        return ret;
521                }
522        }
523
524        /*****************************************************************************
525         * Other utility methods
526         */
527
528        private static File makePackageDir(WsimportOptions options) {
529                File ret = null;
530                if (options.defaultPackage != null && !options.defaultPackage.equals("")) {
531                        String subDir = options.defaultPackage.replace('.', '/');
532                        ret = new File(options.implDestDir, subDir);
533                } else {
534                        ret = options.implDestDir;
535                }
536
537                boolean created = ret.mkdirs();
538                if (options.verbose && !created) {
539                    System.out.println(MessageFormat.format("Directory not created: {0}", ret));
540                }
541                return ret;
542        }
543
544        private static String getQualifiedFileName(String canonicalBaseDir, File f)
545            throws java.io.IOException {
546                String fp = f.getCanonicalPath();
547                if (fp == null)
548                        return null;
549                fp = fp.replace(canonicalBaseDir, "");
550                fp = fp.replace('\\', '.');
551                fp = fp.replace('/', '.');
552                if (fp.startsWith("."))
553                        fp = fp.substring(1);
554
555                return fp;
556        }
557
558        private static File findFile(WsimportOptions options, String qualifiedFileName)
559            throws java.io.IOException {
560                String baseDir = options.sourceDir.getCanonicalPath();
561                String fp = null;
562                for (File f : options.getGeneratedFiles()) {
563                        fp = getQualifiedFileName(baseDir, f);
564                        if (qualifiedFileName.equals(fp))
565                                return f;
566                }
567
568                return null;
569        }
570
571        private static boolean equalsNSOptional(String strQName, QName checkQN) {
572                if (strQName == null)
573                        return false;
574                strQName = strQName.trim();
575                QName reqQN = QName.valueOf(strQName);
576
577                if (reqQN.getNamespaceURI() == null || reqQN.getNamespaceURI().equals(""))
578                        return reqQN.getLocalPart().equals(checkQN.getLocalPart());
579
580                return reqQN.equals(checkQN);
581        }
582}
583