1/*
2 * Copyright (c) 2010, 2016, 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.modeler.annotation;
27
28import com.sun.istack.internal.logging.Logger;
29import com.sun.tools.internal.ws.processor.generator.GeneratorUtil;
30import com.sun.tools.internal.ws.processor.modeler.ModelerException;
31import com.sun.tools.internal.ws.resources.WebserviceapMessages;
32import com.sun.tools.internal.ws.wscompile.AbortException;
33import com.sun.tools.internal.ws.wscompile.WsgenOptions;
34
35import javax.annotation.processing.AbstractProcessor;
36import javax.annotation.processing.ProcessingEnvironment;
37import javax.annotation.processing.RoundEnvironment;
38import javax.annotation.processing.SupportedAnnotationTypes;
39import javax.annotation.processing.SupportedOptions;
40import javax.jws.WebService;
41import javax.lang.model.SourceVersion;
42import javax.lang.model.element.Element;
43import javax.lang.model.element.ElementKind;
44import javax.lang.model.element.Name;
45import javax.lang.model.element.TypeElement;
46import javax.lang.model.type.TypeMirror;
47import javax.lang.model.util.ElementFilter;
48import javax.tools.Diagnostic;
49import javax.xml.ws.Holder;
50import javax.xml.ws.WebServiceProvider;
51import java.io.ByteArrayOutputStream;
52import java.io.File;
53import java.io.PrintStream;
54import java.util.ArrayList;
55import java.util.Collection;
56import java.util.HashSet;
57import java.util.Set;
58import java.util.logging.Level;
59
60/**
61 * WebServiceAp is a AnnotationProcessor for processing javax.jws.* and
62 * javax.xml.ws.* annotations. This class is used either by the WsGen (CompileTool) tool or
63 * indirectly when invoked by javac.
64 *
65 * @author WS Development Team
66 */
67@SupportedAnnotationTypes({
68        "javax.jws.HandlerChain",
69        "javax.jws.Oneway",
70        "javax.jws.WebMethod",
71        "javax.jws.WebParam",
72        "javax.jws.WebResult",
73        "javax.jws.WebService",
74        "javax.jws.soap.InitParam",
75        "javax.jws.soap.SOAPBinding",
76        "javax.jws.soap.SOAPMessageHandler",
77        "javax.jws.soap.SOAPMessageHandlers",
78        "javax.xml.ws.BindingType",
79        "javax.xml.ws.RequestWrapper",
80        "javax.xml.ws.ResponseWrapper",
81        "javax.xml.ws.ServiceMode",
82        "javax.xml.ws.WebEndpoint",
83        "javax.xml.ws.WebFault",
84        "javax.xml.ws.WebServiceClient",
85        "javax.xml.ws.WebServiceProvider",
86        "javax.xml.ws.WebServiceRef"
87})
88@SupportedOptions({WebServiceAp.DO_NOT_OVERWRITE, WebServiceAp.IGNORE_NO_WEB_SERVICE_FOUND_WARNING, WebServiceAp.VERBOSE})
89public class WebServiceAp extends AbstractProcessor implements ModelBuilder {
90
91    private static final Logger LOGGER = Logger.getLogger(WebServiceAp.class);
92
93    public static final String DO_NOT_OVERWRITE = "doNotOverWrite";
94    public static final String IGNORE_NO_WEB_SERVICE_FOUND_WARNING = "ignoreNoWebServiceFoundWarning";
95    public static final String VERBOSE = "verbose";
96
97    private WsgenOptions options;
98    protected AnnotationProcessorContext context;
99    private File sourceDir;
100    private boolean doNotOverWrite;
101    private boolean ignoreNoWebServiceFoundWarning = false;
102    private TypeMirror exceptionElement;
103    private TypeMirror runtimeExceptionElement;
104    private TypeElement defHolderElement;
105    private boolean isCommandLineInvocation;
106    private PrintStream out;
107    private Collection<TypeElement> processedTypeElements = new HashSet<TypeElement>();
108
109    public WebServiceAp() {
110        this.context = new AnnotationProcessorContext();
111    }
112
113    public WebServiceAp(WsgenOptions options, PrintStream out) {
114        this.options = options;
115        this.sourceDir = (options != null) ? options.sourceDir : null;
116        this.doNotOverWrite = (options != null) && options.doNotOverWrite;
117        this.context = new AnnotationProcessorContext();
118        this.out = out;
119    }
120
121    @Override
122    public synchronized void init(ProcessingEnvironment processingEnv) {
123        super.init(processingEnv);
124        exceptionElement = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType();
125        runtimeExceptionElement = processingEnv.getElementUtils().getTypeElement(RuntimeException.class.getName()).asType();
126        defHolderElement = processingEnv.getElementUtils().getTypeElement(Holder.class.getName());
127        if (options == null) {
128            options = new WsgenOptions();
129
130            out = new PrintStream(new ByteArrayOutputStream());
131            doNotOverWrite = getOption(DO_NOT_OVERWRITE);
132            ignoreNoWebServiceFoundWarning = getOption(IGNORE_NO_WEB_SERVICE_FOUND_WARNING);
133            options.verbose = getOption(VERBOSE);
134            isCommandLineInvocation = true;
135        }
136        options.filer = processingEnv.getFiler();
137    }
138
139    private boolean getOption(String key) {
140        String value = processingEnv.getOptions().get(key);
141        if (value != null) {
142            return Boolean.valueOf(value);
143        }
144        return false;
145    }
146
147    @Override
148    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
149        if (context.getRound() != 1) {
150            return true;
151        }
152        context.incrementRound();
153        WebService webService;
154        WebServiceProvider webServiceProvider;
155        WebServiceVisitor webServiceVisitor = new WebServiceWrapperGenerator(this, context);
156        boolean processedEndpoint = false;
157        Collection<TypeElement> classes = new ArrayList<TypeElement>();
158        filterClasses(classes, roundEnv.getRootElements());
159        for (TypeElement element : classes) {
160            webServiceProvider = element.getAnnotation(WebServiceProvider.class);
161            webService = element.getAnnotation(WebService.class);
162            if (webServiceProvider != null) {
163                if (webService != null) {
164                    processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_AND_WEBSERVICEPROVIDER(element.getQualifiedName()));
165                }
166                processedEndpoint = true;
167            }
168
169            if (webService == null) {
170                continue;
171            }
172
173            element.accept(webServiceVisitor, null);
174            processedEndpoint = true;
175        }
176        if (!processedEndpoint) {
177            if (isCommandLineInvocation) {
178                if (!ignoreNoWebServiceFoundWarning) {
179                    processWarning(WebserviceapMessages.WEBSERVICEAP_NO_WEBSERVICE_ENDPOINT_FOUND());
180                }
181            } else {
182                processError(WebserviceapMessages.WEBSERVICEAP_NO_WEBSERVICE_ENDPOINT_FOUND());
183            }
184        }
185        return true;
186    }
187
188    private void filterClasses(Collection<TypeElement> classes, Collection<? extends Element> elements) {
189        for (Element element : elements) {
190            if (element.getKind().equals(ElementKind.CLASS)) {
191                classes.add((TypeElement) element);
192                filterClasses(classes, ElementFilter.typesIn(element.getEnclosedElements()));
193            }
194        }
195    }
196
197    @Override
198    public void processWarning(String message) {
199        if (isCommandLineInvocation) {
200            processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, message);
201        } else {
202            report(message);
203        }
204    }
205
206    protected void report(String msg) {
207        if (out == null) {
208            if (LOGGER.isLoggable(Level.FINE)) {
209                LOGGER.log(Level.FINE, "No output set for web service annotation processor reporting.");
210            }
211            return;
212        }
213        out.println(msg);
214        out.flush();
215    }
216
217    @Override
218    public void processError(String message) {
219        if (isCommandLineInvocation) {
220            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message);
221            throw new AbortException();
222        } else {
223            throw new ModelerException(message);
224        }
225    }
226
227    @Override
228    public void processError(String message, Element element) {
229        if (isCommandLineInvocation) {
230            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message, element);
231        } else {
232            throw new ModelerException(message);
233        }
234    }
235
236    @Override
237    public boolean canOverWriteClass(String className) {
238        return !((doNotOverWrite && GeneratorUtil.classExists(options, className)));
239    }
240
241    @Override
242    public File getSourceDir() {
243        return sourceDir;
244    }
245
246    @Override
247    public boolean isRemote(TypeElement typeElement) {
248        return TypeModeler.isRemote(typeElement);
249    }
250
251    @Override
252    public boolean isServiceException(TypeMirror typeMirror) {
253        return processingEnv.getTypeUtils().isSubtype(typeMirror, exceptionElement)
254                && !processingEnv.getTypeUtils().isSubtype(typeMirror, runtimeExceptionElement)
255                && !TypeModeler.isRemoteException(processingEnv, typeMirror);
256    }
257
258    @Override
259    public TypeMirror getHolderValueType(TypeMirror type) {
260        return TypeModeler.getHolderValueType(type, defHolderElement, processingEnv);
261    }
262
263    @Override
264    public boolean checkAndSetProcessed(TypeElement typeElement) {
265        if (!processedTypeElements.contains(typeElement)) {
266            processedTypeElements.add(typeElement);
267            return false;
268        }
269        return true;
270    }
271
272    @Override
273    public void log(String message) {
274        if (options != null && options.verbose) {
275            message = new StringBuilder().append('[').append(message).append(']').toString(); // "[%s]"
276            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message);
277        }
278    }
279
280    @Override
281    public WsgenOptions getOptions() {
282        return options;
283    }
284
285    @Override
286    public ProcessingEnvironment getProcessingEnvironment() {
287        return processingEnv;
288    }
289
290    @Override
291    public String getOperationName(Name messageName) {
292        return messageName != null ? messageName.toString() : null;
293    }
294
295    @Override
296    public SourceVersion getSupportedSourceVersion() {
297        return SourceVersion.latest();
298    }
299}
300