/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.internal.ws.processor.modeler.annotation; import com.sun.istack.internal.logging.Logger; import com.sun.tools.internal.ws.processor.generator.GeneratorUtil; import com.sun.tools.internal.ws.processor.modeler.ModelerException; import com.sun.tools.internal.ws.resources.WebserviceapMessages; import com.sun.tools.internal.ws.wscompile.AbortException; import com.sun.tools.internal.ws.wscompile.WsgenOptions; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedOptions; import javax.jws.WebService; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic; import javax.xml.ws.Holder; import javax.xml.ws.WebServiceProvider; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintStream; import java.lang.reflect.Method; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Scanner; import java.util.Set; import java.util.logging.Level; /** * WebServiceAp is a AnnotationProcessor for processing javax.jws.* and * javax.xml.ws.* annotations. This class is used either by the WsGen (CompileTool) tool or * indirectly when invoked by javac. * * @author WS Development Team */ @SupportedAnnotationTypes({ "javax.jws.HandlerChain", "javax.jws.Oneway", "javax.jws.WebMethod", "javax.jws.WebParam", "javax.jws.WebResult", "javax.jws.WebService", "javax.jws.soap.InitParam", "javax.jws.soap.SOAPBinding", "javax.jws.soap.SOAPMessageHandler", "javax.jws.soap.SOAPMessageHandlers", "javax.xml.ws.BindingType", "javax.xml.ws.RequestWrapper", "javax.xml.ws.ResponseWrapper", "javax.xml.ws.ServiceMode", "javax.xml.ws.WebEndpoint", "javax.xml.ws.WebFault", "javax.xml.ws.WebServiceClient", "javax.xml.ws.WebServiceProvider", "javax.xml.ws.WebServiceRef" }) @SupportedOptions({WebServiceAp.DO_NOT_OVERWRITE, WebServiceAp.IGNORE_NO_WEB_SERVICE_FOUND_WARNING}) public class WebServiceAp extends AbstractProcessor implements ModelBuilder { private static final Logger LOGGER = Logger.getLogger(WebServiceAp.class); public static final String DO_NOT_OVERWRITE = "doNotOverWrite"; public static final String IGNORE_NO_WEB_SERVICE_FOUND_WARNING = "ignoreNoWebServiceFoundWarning"; private WsgenOptions options; protected AnnotationProcessorContext context; private File sourceDir; private boolean doNotOverWrite; private boolean ignoreNoWebServiceFoundWarning = false; private TypeElement remoteElement; private TypeMirror remoteExceptionElement; private TypeMirror exceptionElement; private TypeMirror runtimeExceptionElement; private TypeElement defHolderElement; private boolean isCommandLineInvocation; private PrintStream out; private Collection processedTypeElements = new HashSet(); public WebServiceAp() { this.context = new AnnotationProcessorContext(); } public WebServiceAp(WsgenOptions options, PrintStream out) { this.options = options; this.sourceDir = (options != null) ? options.sourceDir : null; this.doNotOverWrite = (options != null) && options.doNotOverWrite; this.context = new AnnotationProcessorContext(); this.out = out; } @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); remoteElement = processingEnv.getElementUtils().getTypeElement(Remote.class.getName()); remoteExceptionElement = processingEnv.getElementUtils().getTypeElement(RemoteException.class.getName()).asType(); exceptionElement = processingEnv.getElementUtils().getTypeElement(Exception.class.getName()).asType(); runtimeExceptionElement = processingEnv.getElementUtils().getTypeElement(RuntimeException.class.getName()).asType(); defHolderElement = processingEnv.getElementUtils().getTypeElement(Holder.class.getName()); if (options == null) { options = new WsgenOptions(); out = new PrintStream(new ByteArrayOutputStream()); doNotOverWrite = getOption(DO_NOT_OVERWRITE); ignoreNoWebServiceFoundWarning = getOption(IGNORE_NO_WEB_SERVICE_FOUND_WARNING); String classDir = parseArguments(); String property = System.getProperty("java.class.path"); options.classpath = classDir + File.pathSeparator + (property != null ? property : ""); isCommandLineInvocation = true; } options.filer = processingEnv.getFiler(); } private String parseArguments() { // let's try to parse JavacOptions String classDir = null; try { ClassLoader cl = WebServiceAp.class.getClassLoader(); Class javacProcessingEnvironmentClass = Class.forName("com.sun.tools.javac.processing.JavacProcessingEnvironment", false, cl); if (javacProcessingEnvironmentClass.isInstance(processingEnv)) { Method getContextMethod = javacProcessingEnvironmentClass.getDeclaredMethod("getContext"); Object tmpContext = getContextMethod.invoke(processingEnv); Class optionsClass = Class.forName("com.sun.tools.javac.util.Options", false, cl); Class contextClass = Class.forName("com.sun.tools.javac.util.Context", false, cl); Method instanceMethod = optionsClass.getDeclaredMethod("instance", new Class[]{contextClass}); Object tmpOptions = instanceMethod.invoke(null, tmpContext); if (tmpOptions != null) { Method getMethod = optionsClass.getDeclaredMethod("get", new Class[]{String.class}); Object result = getMethod.invoke(tmpOptions, "-s"); // todo: we have to check for -d also if (result != null) { classDir = (String) result; } this.options.verbose = getMethod.invoke(tmpOptions, "-verbose") != null; } } } catch (Exception e) { /// some Error was here - problems with reflection or security processWarning(WebserviceapMessages.WEBSERVICEAP_PARSING_JAVAC_OPTIONS_ERROR()); report(e.getMessage()); } if (classDir == null) { // some error within reflection block String property = System.getProperty("sun.java.command"); if (property != null) { Scanner scanner = new Scanner(property); boolean sourceDirNext = false; while (scanner.hasNext()) { String token = scanner.next(); if (sourceDirNext) { classDir = token; sourceDirNext = false; } else if ("-verbose".equals(token)) { options.verbose = true; } else if ("-s".equals(token)) { sourceDirNext = true; } } } } if (classDir != null) { sourceDir = new File(classDir); } return classDir; } private boolean getOption(String key) { String value = processingEnv.getOptions().get(key); if (value != null) { return Boolean.valueOf(value); } return false; } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { if (context.getRound() != 1) { return true; } context.incrementRound(); WebService webService; WebServiceProvider webServiceProvider; WebServiceVisitor webServiceVisitor = new WebServiceWrapperGenerator(this, context); boolean processedEndpoint = false; Collection classes = new ArrayList(); filterClasses(classes, roundEnv.getRootElements()); for (TypeElement element : classes) { webServiceProvider = element.getAnnotation(WebServiceProvider.class); webService = element.getAnnotation(WebService.class); if (webServiceProvider != null) { if (webService != null) { processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_AND_WEBSERVICEPROVIDER(element.getQualifiedName())); } processedEndpoint = true; } if (webService == null) { continue; } element.accept(webServiceVisitor, null); processedEndpoint = true; } if (!processedEndpoint) { if (isCommandLineInvocation) { if (!ignoreNoWebServiceFoundWarning) { processWarning(WebserviceapMessages.WEBSERVICEAP_NO_WEBSERVICE_ENDPOINT_FOUND()); } } else { processError(WebserviceapMessages.WEBSERVICEAP_NO_WEBSERVICE_ENDPOINT_FOUND()); } } return true; } private void filterClasses(Collection classes, Collection elements) { for (Element element : elements) { if (element.getKind().equals(ElementKind.CLASS)) { classes.add((TypeElement) element); filterClasses(classes, ElementFilter.typesIn(element.getEnclosedElements())); } } } @Override public void processWarning(String message) { if (isCommandLineInvocation) { processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, message); } else { report(message); } } protected void report(String msg) { if (out == null) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "No output set for web service annotation processor reporting."); } return; } out.println(msg); out.flush(); } @Override public void processError(String message) { if (isCommandLineInvocation) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message); throw new AbortException(); } else { throw new ModelerException(message); } } @Override public void processError(String message, Element element) { if (isCommandLineInvocation) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message, element); } else { throw new ModelerException(message); } } @Override public boolean canOverWriteClass(String className) { return !((doNotOverWrite && GeneratorUtil.classExists(options, className))); } @Override public File getSourceDir() { return sourceDir; } @Override public boolean isRemote(TypeElement typeElement) { return processingEnv.getTypeUtils().isSubtype(typeElement.asType(), remoteElement.asType()); } @Override public boolean isServiceException(TypeMirror typeMirror) { return processingEnv.getTypeUtils().isSubtype(typeMirror, exceptionElement) && !processingEnv.getTypeUtils().isSubtype(typeMirror, runtimeExceptionElement) && !processingEnv.getTypeUtils().isSubtype(typeMirror, remoteExceptionElement); } @Override public TypeMirror getHolderValueType(TypeMirror type) { return TypeModeler.getHolderValueType(type, defHolderElement, processingEnv); } @Override public boolean checkAndSetProcessed(TypeElement typeElement) { if (!processedTypeElements.contains(typeElement)) { processedTypeElements.add(typeElement); return false; } return true; } @Override public void log(String message) { if (options != null && options.verbose) { message = new StringBuilder().append('[').append(message).append(']').toString(); // "[%s]" processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message); } } @Override public WsgenOptions getOptions() { return options; } @Override public ProcessingEnvironment getProcessingEnvironment() { return processingEnv; } @Override public String getOperationName(Name messageName) { return messageName != null ? messageName.toString() : null; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } }