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 
  26 package com.sun.tools.internal.ws.processor.modeler.annotation;
  27 
  28 import com.sun.tools.internal.ws.processor.model.Port;
  29 import com.sun.tools.internal.ws.resources.WebserviceapMessages;
  30 import com.sun.tools.internal.ws.util.ClassNameInfo;
  31 import com.sun.tools.internal.ws.wsdl.document.soap.SOAPStyle;
  32 import com.sun.xml.internal.ws.model.RuntimeModeler;
  33 
  34 import javax.annotation.processing.ProcessingEnvironment;
  35 import javax.jws.Oneway;
  36 import javax.jws.WebMethod;
  37 import javax.jws.WebParam;
  38 import javax.jws.WebResult;
  39 import javax.jws.WebService;
  40 import javax.jws.soap.SOAPBinding;
  41 import javax.jws.soap.SOAPBinding.ParameterStyle;
  42 import javax.lang.model.element.Element;
  43 import javax.lang.model.element.ElementKind;
  44 import javax.lang.model.element.ExecutableElement;
  45 import javax.lang.model.element.Modifier;
  46 import javax.lang.model.element.Name;
  47 import javax.lang.model.element.PackageElement;
  48 import javax.lang.model.element.TypeElement;
  49 import javax.lang.model.element.VariableElement;
  50 import javax.lang.model.type.DeclaredType;
  51 import javax.lang.model.type.NoType;
  52 import javax.lang.model.type.TypeKind;
  53 import javax.lang.model.type.TypeMirror;
  54 import javax.lang.model.util.ElementFilter;
  55 import javax.lang.model.util.SimpleElementVisitor6;
  56 import javax.lang.model.util.SimpleTypeVisitor6;
  57 import javax.lang.model.util.Types;
  58 import java.lang.annotation.Annotation;
  59 import java.util.Collection;
  60 import java.util.HashSet;
  61 import java.util.List;
  62 import java.util.Set;
  63 import java.util.Stack;
  64 
  65 /**
  66  * @author WS Development Team
  67  */
  68 public abstract class WebServiceVisitor extends SimpleElementVisitor6<Void, Object> {
  69 
  70     protected ModelBuilder builder;
  71     protected String wsdlNamespace;
  72     protected String typeNamespace;
  73     protected Stack<SOAPBinding> soapBindingStack;
  74     protected SOAPBinding typeElementSoapBinding;
  75     protected SOAPStyle soapStyle = SOAPStyle.DOCUMENT;
  76     protected boolean wrapped = true;
  77     protected Port port;
  78     protected Name serviceImplName;
  79     protected Name endpointInterfaceName;
  80     protected AnnotationProcessorContext context;
  81     protected AnnotationProcessorContext.SeiContext seiContext;
  82     protected boolean processingSei = false;
  83     protected String serviceName;
  84     protected Name packageName;
  85     protected String portName;
  86     protected boolean endpointReferencesInterface = false;
  87     protected boolean hasWebMethods = false;
  88     protected TypeElement typeElement;
  89     protected Set<String> processedMethods;
  90     protected boolean pushedSoapBinding = false;
  91 
  92     private static final NoTypeVisitor NO_TYPE_VISITOR = new NoTypeVisitor();
  93 
  94     public WebServiceVisitor(ModelBuilder builder, AnnotationProcessorContext context) {
  95         this.builder = builder;
  96         this.context = context;
  97         soapBindingStack = new Stack<SOAPBinding>();
  98         processedMethods = new HashSet<String>();
  99     }
 100 
 101     @Override
 102     public Void visitType(TypeElement e, Object o) {
 103         WebService webService = e.getAnnotation(WebService.class);
 104         if (!shouldProcessWebService(webService, e))
 105             return null;
 106         if (builder.checkAndSetProcessed(e))
 107             return null;
 108         typeElement = e;
 109 
 110         switch (e.getKind()) {
 111             case INTERFACE: {
 112                 if (endpointInterfaceName != null && !endpointInterfaceName.equals(e.getQualifiedName())) {
 113                     builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTERFACES_DO_NOT_MATCH(endpointInterfaceName, e.getQualifiedName()), e);
 114                 }
 115                 verifySeiAnnotations(webService, e);
 116                 endpointInterfaceName = e.getQualifiedName();
 117                 processingSei = true;
 118                 preProcessWebService(webService, e);
 119                 processWebService(webService, e);
 120                 postProcessWebService(webService, e);
 121                 break;
 122             }
 123             case CLASS: {
 124                 typeElementSoapBinding = e.getAnnotation(SOAPBinding.class);
 125                 if (serviceImplName == null)
 126                     serviceImplName = e.getQualifiedName();
 127                 String endpointInterfaceName = webService != null ? webService.endpointInterface() : null;
 128                 if (endpointInterfaceName != null && endpointInterfaceName.length() > 0) {
 129                     checkForInvalidImplAnnotation(e, SOAPBinding.class);
 130                     if (webService.name().length() > 0)
 131                         builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTEFACE_PLUS_ELEMENT("name"), e);
 132                     endpointReferencesInterface = true;
 133                     verifyImplAnnotations(e);
 134                     inspectEndpointInterface(endpointInterfaceName, e);
 135                     serviceImplName = null;
 136                     return null;
 137                 }
 138                 processingSei = false;
 139                 preProcessWebService(webService, e);
 140                 processWebService(webService, e);
 141                 serviceImplName = null;
 142                 postProcessWebService(webService, e);
 143                 serviceImplName = null;
 144                 break;
 145             }
 146             default:
 147                 break;
 148         }
 149         return null;
 150     }
 151 
 152     protected void verifySeiAnnotations(WebService webService, TypeElement d) {
 153         if (webService.endpointInterface().length() > 0) {
 154             builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTERFACE_ON_INTERFACE(
 155                     d.getQualifiedName(), webService.endpointInterface()), d);
 156         }
 157         if (webService.serviceName().length() > 0) {
 158             builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SEI_ANNOTATION_ELEMENT(
 159                     "serviceName", d.getQualifiedName()), d);
 160         }
 161         if (webService.portName().length() > 0) {
 162             builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SEI_ANNOTATION_ELEMENT(
 163                     "portName", d.getQualifiedName()), d);
 164         }
 165     }
 166 
 167     protected void verifyImplAnnotations(TypeElement d) {
 168         for (ExecutableElement method : ElementFilter.methodsIn(d.getEnclosedElements())) {
 169             checkForInvalidImplAnnotation(method, WebMethod.class);
 170             checkForInvalidImplAnnotation(method, Oneway.class);
 171             checkForInvalidImplAnnotation(method, WebResult.class);
 172             for (VariableElement param : method.getParameters()) {
 173                 checkForInvalidImplAnnotation(param, WebParam.class);
 174             }
 175         }
 176     }
 177 
 178     protected void checkForInvalidSeiAnnotation(TypeElement element, Class annotationClass) {
 179         Object annotation = element.getAnnotation(annotationClass);
 180         if (annotation != null) {
 181             builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SEI_ANNOTATION(
 182                     annotationClass.getName(), element.getQualifiedName()), element);
 183         }
 184     }
 185 
 186     protected void checkForInvalidImplAnnotation(Element element, Class annotationClass) {
 187         Object annotation = element.getAnnotation(annotationClass);
 188         if (annotation != null) {
 189             builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTEFACE_PLUS_ANNOTATION(annotationClass.getName()), element);
 190         }
 191     }
 192 
 193     protected void preProcessWebService(WebService webService, TypeElement element) {
 194         processedMethods = new HashSet<String>();
 195         seiContext = context.getSeiContext(element);
 196         String targetNamespace = null;
 197         if (webService != null)
 198             targetNamespace = webService.targetNamespace();
 199         PackageElement packageElement = builder.getProcessingEnvironment().getElementUtils().getPackageOf(element);
 200         if (targetNamespace == null || targetNamespace.length() == 0) {
 201             String packageName = packageElement.getQualifiedName().toString();
 202             if (packageName == null || packageName.length() == 0) {
 203                 builder.processError(WebserviceapMessages.WEBSERVICEAP_NO_PACKAGE_CLASS_MUST_HAVE_TARGETNAMESPACE(
 204                         element.getQualifiedName()), element);
 205             }
 206             targetNamespace = RuntimeModeler.getNamespace(packageName);
 207         }
 208         seiContext.setNamespaceUri(targetNamespace);
 209         if (serviceImplName == null)
 210             serviceImplName = seiContext.getSeiImplName();
 211         if (serviceImplName != null) {
 212             seiContext.setSeiImplName(serviceImplName);
 213             context.addSeiContext(serviceImplName, seiContext);
 214         }
 215         portName = ClassNameInfo.getName(element.getSimpleName().toString().replace('$', '_'));
 216         packageName = packageElement.getQualifiedName();
 217         portName = webService != null && webService.name() != null && webService.name().length() > 0 ?
 218                 webService.name() : portName;
 219         serviceName = ClassNameInfo.getName(element.getQualifiedName().toString()) + WebServiceConstants.SERVICE.getValue();
 220         serviceName = webService != null && webService.serviceName() != null && webService.serviceName().length() > 0 ?
 221                 webService.serviceName() : serviceName;
 222         wsdlNamespace = seiContext.getNamespaceUri();
 223         typeNamespace = wsdlNamespace;
 224 
 225         SOAPBinding soapBinding = element.getAnnotation(SOAPBinding.class);
 226         if (soapBinding != null) {
 227             pushedSoapBinding = pushSoapBinding(soapBinding, element, element);
 228         } else if (element.equals(typeElement)) {
 229             pushedSoapBinding = pushSoapBinding(new MySoapBinding(), element, element);
 230         }
 231     }
 232 
 233     public static boolean sameStyle(SOAPBinding.Style style, SOAPStyle soapStyle) {
 234         return style.equals(SOAPBinding.Style.DOCUMENT)
 235                 && soapStyle.equals(SOAPStyle.DOCUMENT)
 236                 || style.equals(SOAPBinding.Style.RPC)
 237                 && soapStyle.equals(SOAPStyle.RPC);
 238     }
 239 
 240     protected boolean pushSoapBinding(SOAPBinding soapBinding, Element bindingElement, TypeElement classElement) {
 241         boolean changed = false;
 242         if (!sameStyle(soapBinding.style(), soapStyle)) {
 243             changed = true;
 244             if (pushedSoapBinding)
 245                 builder.processError(WebserviceapMessages.WEBSERVICEAP_MIXED_BINDING_STYLE(
 246                         classElement.getQualifiedName()), bindingElement);
 247         }
 248         if (soapBinding.style().equals(SOAPBinding.Style.RPC)) {
 249             soapStyle = SOAPStyle.RPC;
 250             wrapped = true;
 251             if (soapBinding.parameterStyle().equals(ParameterStyle.BARE)) {
 252                 builder.processError(WebserviceapMessages.WEBSERVICEAP_RPC_LITERAL_MUST_NOT_BE_BARE(
 253                         classElement.getQualifiedName()), bindingElement);
 254             }
 255         } else {
 256             soapStyle = SOAPStyle.DOCUMENT;
 257             if (wrapped != soapBinding.parameterStyle().equals(ParameterStyle.WRAPPED)) {
 258                 wrapped = soapBinding.parameterStyle().equals(ParameterStyle.WRAPPED);
 259                 changed = true;
 260             }
 261         }
 262         if (soapBinding.use().equals(SOAPBinding.Use.ENCODED)) {
 263             String style = "rpc";
 264             if (soapBinding.style().equals(SOAPBinding.Style.DOCUMENT))
 265                 style = "document";
 266             builder.processError(WebserviceapMessages.WEBSERVICE_ENCODED_NOT_SUPPORTED(
 267                     classElement.getQualifiedName(), style), bindingElement);
 268         }
 269         if (changed || soapBindingStack.empty()) {
 270             soapBindingStack.push(soapBinding);
 271             pushedSoapBinding = true;
 272         }
 273         return changed;
 274     }
 275 
 276     protected SOAPBinding popSoapBinding() {
 277         if (pushedSoapBinding)
 278             soapBindingStack.pop();
 279         SOAPBinding soapBinding = null;
 280         if (!soapBindingStack.empty()) {
 281             soapBinding = soapBindingStack.peek();
 282             if (soapBinding.style().equals(SOAPBinding.Style.RPC)) {
 283                 soapStyle = SOAPStyle.RPC;
 284                 wrapped = true;
 285             } else {
 286                 soapStyle = SOAPStyle.DOCUMENT;
 287                 wrapped = soapBinding.parameterStyle().equals(ParameterStyle.WRAPPED);
 288             }
 289         } else {
 290                 pushedSoapBinding = false;
 291         }
 292         return soapBinding;
 293     }
 294 
 295     protected String getNamespace(PackageElement packageElement) {
 296         return RuntimeModeler.getNamespace(packageElement.getQualifiedName().toString());
 297     }
 298 
 299     protected boolean shouldProcessWebService(WebService webService, TypeElement element) {
 300         switch (element.getKind()) {
 301             case INTERFACE: {
 302                 hasWebMethods = false;
 303                 if (webService == null)
 304                     builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTERFACE_HAS_NO_WEBSERVICE_ANNOTATION(
 305                             element.getQualifiedName()), element);
 306 
 307                 SOAPBinding soapBinding = element.getAnnotation(SOAPBinding.class);
 308                 if (soapBinding != null
 309                         && soapBinding.style() == SOAPBinding.Style.RPC
 310                         && soapBinding.parameterStyle() == SOAPBinding.ParameterStyle.BARE) {
 311                     builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SOAPBINDING_PARAMETERSTYLE(
 312                             soapBinding, element), element);
 313                     return false;
 314                 }
 315                 return isLegalSei(element);
 316             }
 317             case CLASS: {
 318                 if (webService == null)
 319                     return false;
 320                 hasWebMethods = hasWebMethods(element);
 321                 SOAPBinding soapBinding = element.getAnnotation(SOAPBinding.class);
 322                 if (soapBinding != null
 323                         && soapBinding.style() == SOAPBinding.Style.RPC
 324                         && soapBinding.parameterStyle() == SOAPBinding.ParameterStyle.BARE) {
 325                     builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SOAPBINDING_PARAMETERSTYLE(
 326                             soapBinding, element), element);
 327                     return false;
 328                 }
 329                 return isLegalImplementation(webService, element);
 330             }
 331             default: {
 332                 throw new IllegalArgumentException("Class or Interface was expecting. But element: " + element);
 333             }
 334         }
 335     }
 336 
 337     abstract protected void processWebService(WebService webService, TypeElement element);
 338 
 339     protected void postProcessWebService(WebService webService, TypeElement element) {
 340         processMethods(element);
 341         popSoapBinding();
 342     }
 343 
 344     protected boolean hasWebMethods(TypeElement element) {
 345         if (element.getQualifiedName().toString().equals(Object.class.getName()))
 346             return false;
 347         WebMethod webMethod;
 348         for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
 349             webMethod = method.getAnnotation(WebMethod.class);
 350             if (webMethod != null) {
 351                 if (webMethod.exclude()) {
 352                     if (webMethod.operationName().length() > 0)
 353                         builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_WEBMETHOD_ELEMENT_WITH_EXCLUDE(
 354                                 "operationName", element.getQualifiedName(), method.toString()), method);
 355                     if (webMethod.action().length() > 0)
 356                         builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_WEBMETHOD_ELEMENT_WITH_EXCLUDE(
 357                                 "action", element.getQualifiedName(), method.toString()), method);
 358                 } else {
 359                     return true;
 360                 }
 361             }
 362         }
 363         return false;//hasWebMethods(d.getSuperclass().getDeclaration());
 364     }
 365 
 366     protected void processMethods(TypeElement element) {
 367         switch (element.getKind()) {
 368             case INTERFACE: {
 369                 builder.log("ProcessedMethods Interface: " + element);
 370                 hasWebMethods = false;
 371                 for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
 372                     method.accept(this, null);
 373                 }
 374                 for (TypeMirror superType : element.getInterfaces())
 375                     processMethods((TypeElement) ((DeclaredType) superType).asElement());
 376                 break;
 377             }
 378             case CLASS: {
 379                 builder.log("ProcessedMethods Class: " + element);
 380                 hasWebMethods = hasWebMethods(element);
 381                 if (element.getQualifiedName().toString().equals(Object.class.getName()))
 382                     return;
 383                 if (element.getAnnotation(WebService.class) != null) {
 384                     // Super classes must have @WebService annotations to pick up their methods
 385                     for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
 386                         method.accept(this, null);
 387                     }
 388                 }
 389                 TypeMirror superclass = element.getSuperclass();
 390                 if (!superclass.getKind().equals(TypeKind.NONE)) {
 391                     processMethods((TypeElement) ((DeclaredType) superclass).asElement());
 392                 }
 393                 break;
 394             }
 395             default:
 396                 break;
 397         }
 398     }
 399 
 400     private TypeElement getEndpointInterfaceElement(String endpointInterfaceName, TypeElement element) {
 401         TypeElement intTypeElement = null;
 402         for (TypeMirror interfaceType : element.getInterfaces()) {
 403             if (endpointInterfaceName.equals(interfaceType.toString())) {
 404                 intTypeElement = (TypeElement) ((DeclaredType) interfaceType).asElement();
 405                 seiContext = context.getSeiContext(intTypeElement.getQualifiedName());
 406                 assert (seiContext != null);
 407                 seiContext.setImplementsSei(true);
 408                 break;
 409             }
 410         }
 411         if (intTypeElement == null) {
 412             intTypeElement = builder.getProcessingEnvironment().getElementUtils().getTypeElement(endpointInterfaceName);
 413         }
 414         if (intTypeElement == null)
 415             builder.processError(WebserviceapMessages.WEBSERVICEAP_ENDPOINTINTERFACE_CLASS_NOT_FOUND(endpointInterfaceName));
 416         return intTypeElement;
 417     }
 418 
 419     private void inspectEndpointInterface(String endpointInterfaceName, TypeElement d) {
 420         TypeElement intTypeElement = getEndpointInterfaceElement(endpointInterfaceName, d);
 421         if (intTypeElement != null)
 422             intTypeElement.accept(this, null);
 423     }
 424 
 425     @Override
 426     public Void visitExecutable(ExecutableElement method, Object o) {
 427         // Methods must be public
 428         if (!method.getModifiers().contains(Modifier.PUBLIC))
 429             return null;
 430         if (processedMethod(method))
 431             return null;
 432         WebMethod webMethod = method.getAnnotation(WebMethod.class);
 433         if (webMethod != null && webMethod.exclude())
 434             return null;
 435         SOAPBinding soapBinding = method.getAnnotation(SOAPBinding.class);
 436         if (soapBinding == null && !method.getEnclosingElement().equals(typeElement)) {
 437             if (method.getEnclosingElement().getKind().equals(ElementKind.CLASS)) {
 438                 soapBinding = method.getEnclosingElement().getAnnotation(SOAPBinding.class);
 439                 if (soapBinding != null)
 440                     builder.log("using " + method.getEnclosingElement() + "'s SOAPBinding.");
 441                 else {
 442                     soapBinding = new MySoapBinding();
 443                 }
 444             }
 445         }
 446         boolean newBinding = false;
 447         if (soapBinding != null) {
 448             newBinding = pushSoapBinding(soapBinding, method, typeElement);
 449         }
 450         try {
 451             if (shouldProcessMethod(method, webMethod)) {
 452                 processMethod(method, webMethod);
 453             }
 454         } finally {
 455             if (newBinding) {
 456                 popSoapBinding();
 457             }
 458         }
 459         return null;
 460     }
 461 
 462     protected boolean processedMethod(ExecutableElement method) {
 463         String id = method.toString();
 464         if (processedMethods.contains(id))
 465             return true;
 466         processedMethods.add(id);
 467         return false;
 468     }
 469 
 470     protected boolean shouldProcessMethod(ExecutableElement method, WebMethod webMethod) {
 471         builder.log("should process method: " + method.getSimpleName() + " hasWebMethods: " + hasWebMethods + " ");
 472         /*
 473         Fix for https://jax-ws.dev.java.net/issues/show_bug.cgi?id=577
 474         if (hasWebMethods && webMethod == null) {
 475             builder.log("webMethod == null");
 476             return false;
 477         }
 478         */
 479         Collection<Modifier> modifiers = method.getModifiers();
 480         boolean staticFinal = modifiers.contains(Modifier.STATIC) || modifiers.contains(Modifier.FINAL);
 481         if (staticFinal) {
 482             if (webMethod != null) {
 483                 builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_METHOD_IS_STATIC_OR_FINAL(method.getEnclosingElement(),
 484                         method), method);
 485             }
 486             return false;
 487         }
 488         boolean result = (endpointReferencesInterface ||
 489                 method.getEnclosingElement().equals(typeElement) ||
 490                 (method.getEnclosingElement().getAnnotation(WebService.class) != null));
 491         builder.log("endpointReferencesInterface: " + endpointReferencesInterface);
 492         builder.log("declaring class has WebService: " + (method.getEnclosingElement().getAnnotation(WebService.class) != null));
 493         builder.log("returning: " + result);
 494         return result;
 495     }
 496 
 497     abstract protected void processMethod(ExecutableElement method, WebMethod webMethod);
 498 
 499     protected boolean isLegalImplementation(WebService webService, TypeElement classElement) {
 500         boolean isStateful = isStateful(classElement);
 501 
 502         Collection<Modifier> modifiers = classElement.getModifiers();
 503         if (!modifiers.contains(Modifier.PUBLIC)) {
 504             builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_CLASS_NOT_PUBLIC(classElement.getQualifiedName()), classElement);
 505             return false;
 506         }
 507         if (modifiers.contains(Modifier.FINAL) && !isStateful) {
 508             builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_CLASS_IS_FINAL(classElement.getQualifiedName()), classElement);
 509             return false;
 510         }
 511         if (modifiers.contains(Modifier.ABSTRACT) && !isStateful) {
 512             builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_CLASS_IS_ABSTRACT(classElement.getQualifiedName()), classElement);
 513             return false;
 514         }
 515         boolean hasDefaultConstructor = false;
 516         for (ExecutableElement constructor : ElementFilter.constructorsIn(classElement.getEnclosedElements())) {
 517             if (constructor.getModifiers().contains(Modifier.PUBLIC) &&
 518                     constructor.getParameters().isEmpty()) {
 519                 hasDefaultConstructor = true;
 520                 break;
 521             }
 522         }
 523         if (!hasDefaultConstructor && !isStateful) {
 524             if (classElement.getEnclosingElement() != null && !modifiers.contains(Modifier.STATIC)) {
 525                 builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_CLASS_IS_INNERCLASS_NOT_STATIC(
 526                         classElement.getQualifiedName()), classElement);
 527                 return false;
 528             }
 529 
 530             builder.processError(WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_NO_DEFAULT_CONSTRUCTOR(
 531                     classElement.getQualifiedName()), classElement);
 532             return false;
 533         }
 534         if (webService.endpointInterface().isEmpty()) {
 535             if (!methodsAreLegal(classElement))
 536                 return false;
 537         } else {
 538             TypeElement interfaceElement = getEndpointInterfaceElement(webService.endpointInterface(), classElement);
 539             if (!classImplementsSei(classElement, interfaceElement))
 540                 return false;
 541         }
 542 
 543         return true;
 544     }
 545 
 546     private boolean isStateful(TypeElement classElement) {
 547         try {
 548             // We don't want dependency on rt-ha module as its not integrated in JDK
 549             return classElement.getAnnotation((Class<? extends Annotation>) Class.forName("com.sun.xml.internal.ws.developer.Stateful")) != null;
 550         } catch (ClassNotFoundException e) {
 551             //ignore
 552         }
 553         return false;
 554     }
 555 
 556     protected boolean classImplementsSei(TypeElement classElement, TypeElement interfaceElement) {
 557         for (TypeMirror interfaceType : classElement.getInterfaces()) {
 558             if (((DeclaredType) interfaceType).asElement().equals(interfaceElement))
 559                 return true;
 560         }
 561         List<ExecutableElement> classMethods = getClassMethods(classElement);
 562         boolean implementsMethod;
 563         for (ExecutableElement interfaceMethod : ElementFilter.methodsIn(interfaceElement.getEnclosedElements())) {
 564             implementsMethod = false;
 565             for (ExecutableElement classMethod : classMethods) {
 566                 if (sameMethod(interfaceMethod, classMethod)) {
 567                     implementsMethod = true;
 568                     classMethods.remove(classMethod);
 569                     break;
 570                 }
 571             }
 572             if (!implementsMethod) {
 573                 builder.processError(WebserviceapMessages.WEBSERVICEAP_METHOD_NOT_IMPLEMENTED(interfaceElement.getSimpleName(), classElement.getSimpleName(), interfaceMethod), interfaceMethod);
 574                 return false;
 575             }
 576         }
 577         return true;
 578     }
 579 
 580     private static List<ExecutableElement> getClassMethods(TypeElement classElement) {
 581         if (classElement.getQualifiedName().toString().equals(Object.class.getName())) // we don't need Object's methods
 582             return null;
 583         TypeElement superclassElement = (TypeElement) ((DeclaredType) classElement.getSuperclass()).asElement();
 584         List<ExecutableElement> superclassesMethods = getClassMethods(superclassElement);
 585         List<ExecutableElement> classMethods = ElementFilter.methodsIn(classElement.getEnclosedElements());
 586         if (superclassesMethods == null)
 587             return classMethods;
 588         else
 589             superclassesMethods.addAll(classMethods);
 590         return superclassesMethods;
 591     }
 592 
 593     protected boolean sameMethod(ExecutableElement method1, ExecutableElement method2) {
 594         if (!method1.getSimpleName().equals(method2.getSimpleName()))
 595             return false;
 596         Types typeUtils = builder.getProcessingEnvironment().getTypeUtils();
 597         if(!typeUtils.isSameType(method1.getReturnType(), method2.getReturnType())
 598                 && !typeUtils.isSubtype(method2.getReturnType(), method1.getReturnType()))
 599             return false;
 600         List<? extends VariableElement> parameters1 = method1.getParameters();
 601         List<? extends VariableElement> parameters2 = method2.getParameters();
 602         if (parameters1.size() != parameters2.size())
 603             return false;
 604         for (int i = 0; i < parameters1.size(); i++) {
 605             if (!typeUtils.isSameType(parameters1.get(i).asType(), parameters2.get(i).asType()))
 606                 return false;
 607         }
 608         return true;
 609     }
 610 
 611     protected boolean isLegalSei(TypeElement interfaceElement) {
 612         for (VariableElement field : ElementFilter.fieldsIn(interfaceElement.getEnclosedElements()))
 613             if (field.getConstantValue() != null) {
 614                 builder.processError(WebserviceapMessages.WEBSERVICEAP_SEI_CANNOT_CONTAIN_CONSTANT_VALUES(
 615                         interfaceElement.getQualifiedName(), field.getSimpleName()));
 616                 return false;
 617             }
 618         return methodsAreLegal(interfaceElement);
 619     }
 620 
 621     protected boolean methodsAreLegal(TypeElement element) {
 622         switch (element.getKind()) {
 623             case INTERFACE: {
 624                 hasWebMethods = false;
 625                 for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
 626                     if (!isLegalMethod(method, element))
 627                         return false;
 628                 }
 629                 for (TypeMirror superInterface : element.getInterfaces()) {
 630                     if (!methodsAreLegal((TypeElement) ((DeclaredType) superInterface).asElement()))
 631                         return false;
 632                 }
 633                 return true;
 634             }
 635             case CLASS: {
 636                 hasWebMethods = hasWebMethods(element);
 637                 for (ExecutableElement method : ElementFilter.methodsIn(element.getEnclosedElements())) {
 638                     if (!method.getModifiers().contains(Modifier.PUBLIC))
 639                         continue; // let's validate only public methods
 640                     if (!isLegalMethod(method, element))
 641                         return false;
 642                 }
 643                 DeclaredType superClass = (DeclaredType) element.getSuperclass();
 644 
 645                 TypeElement tE = (TypeElement) superClass.asElement();
 646                 return tE.getQualifiedName().toString().equals(Object.class.getName())
 647                         || methodsAreLegal(tE);
 648             }
 649             default: {
 650                 throw new IllegalArgumentException("Class or interface was expecting. But element: " + element);
 651             }
 652         }
 653     }
 654 
 655     protected boolean isLegalMethod(ExecutableElement method, TypeElement typeElement) {
 656         WebMethod webMethod = method.getAnnotation(WebMethod.class);
 657         //SEI cannot have methods with @WebMethod(exclude=true)
 658         if (typeElement.getKind().equals(ElementKind.INTERFACE) && webMethod != null && webMethod.exclude())
 659             builder.processError(WebserviceapMessages.WEBSERVICEAP_INVALID_SEI_ANNOTATION_ELEMENT_EXCLUDE("exclude=true", typeElement.getQualifiedName(), method.toString()), method);
 660         // With https://jax-ws.dev.java.net/issues/show_bug.cgi?id=577, hasWebMethods has no effect
 661         if (hasWebMethods && webMethod == null) // backwards compatibility (for legacyWebMethod computation)
 662             return true;
 663 
 664         if ((webMethod != null) && webMethod.exclude()) {
 665             return true;
 666         }
 667         /*
 668         This check is not needed as Impl class is already checked that it is not abstract.
 669         if (typeElement instanceof TypeElement && method.getModifiers().contains(Modifier.ABSTRACT)) {  // use Kind.equals instead of instanceOf
 670             builder.processError(method.getPosition(), WebserviceapMessages.WEBSERVICEAP_WEBSERVICE_METHOD_IS_ABSTRACT(typeElement.getQualifiedName(), method.getSimpleName()));
 671             return false;
 672         }
 673         */
 674         TypeMirror returnType = method.getReturnType();
 675         if (!isLegalType(returnType)) {
 676             builder.processError(WebserviceapMessages.WEBSERVICEAP_METHOD_RETURN_TYPE_CANNOT_IMPLEMENT_REMOTE(typeElement.getQualifiedName(),
 677                     method.getSimpleName(),
 678                     returnType), method);
 679         }
 680         boolean isOneWay = method.getAnnotation(Oneway.class) != null;
 681         if (isOneWay && !isValidOneWayMethod(method, typeElement))
 682             return false;
 683 
 684         SOAPBinding soapBinding = method.getAnnotation(SOAPBinding.class);
 685         if (soapBinding != null) {
 686             if (soapBinding.style().equals(SOAPBinding.Style.RPC)) {
 687                 builder.processError(WebserviceapMessages.WEBSERVICEAP_RPC_SOAPBINDING_NOT_ALLOWED_ON_METHOD(typeElement.getQualifiedName(), method.toString()), method);
 688             }
 689         }
 690 
 691         int paramIndex = 0;
 692         for (VariableElement parameter : method.getParameters()) {
 693             if (!isLegalParameter(parameter, method, typeElement, paramIndex++))
 694                 return false;
 695         }
 696 
 697         if (!isDocLitWrapped() && soapStyle.equals(SOAPStyle.DOCUMENT)) {
 698             VariableElement outParam = getOutParameter(method);
 699             int inParams = getModeParameterCount(method, WebParam.Mode.IN);
 700             int outParams = getModeParameterCount(method, WebParam.Mode.OUT);
 701             if (inParams != 1) {
 702                 builder.processError(WebserviceapMessages.WEBSERVICEAP_DOC_BARE_AND_NO_ONE_IN(typeElement.getQualifiedName(), method.toString()), method);
 703             }
 704             if (returnType.accept(NO_TYPE_VISITOR, null)) {
 705                 if (outParam == null && !isOneWay) {
 706                     builder.processError(WebserviceapMessages.WEBSERVICEAP_DOC_BARE_NO_OUT(typeElement.getQualifiedName(), method.toString()), method);
 707                 }
 708                 if (outParams != 1) {
 709                     if (!isOneWay && outParams != 0)
 710                         builder.processError(WebserviceapMessages.WEBSERVICEAP_DOC_BARE_NO_RETURN_AND_NO_OUT(typeElement.getQualifiedName(), method.toString()), method);
 711                 }
 712             } else {
 713                 if (outParams > 0) {
 714                     builder.processError(WebserviceapMessages.WEBSERVICEAP_DOC_BARE_RETURN_AND_OUT(typeElement.getQualifiedName(), method.toString()), outParam);
 715                 }
 716             }
 717         }
 718         return true;
 719     }
 720 
 721     protected boolean isLegalParameter(VariableElement param,
 722                                        ExecutableElement method,
 723                                        TypeElement typeElement,
 724                                        int paramIndex) {
 725         if (!isLegalType(param.asType())) {
 726             builder.processError(WebserviceapMessages.WEBSERVICEAP_METHOD_PARAMETER_TYPES_CANNOT_IMPLEMENT_REMOTE(typeElement.getQualifiedName(),
 727                     method.getSimpleName(),
 728                     param.getSimpleName(),
 729                     param.asType().toString()), param);
 730             return false;
 731         }
 732         TypeMirror holderType;
 733         holderType = builder.getHolderValueType(param.asType());
 734         WebParam webParam = param.getAnnotation(WebParam.class);
 735         WebParam.Mode mode = null;
 736         if (webParam != null)
 737             mode = webParam.mode();
 738 
 739         if (holderType != null) {
 740             if (mode != null && mode == WebParam.Mode.IN)
 741                 builder.processError(WebserviceapMessages.WEBSERVICEAP_HOLDER_PARAMETERS_MUST_NOT_BE_IN_ONLY(typeElement.getQualifiedName(), method.toString(), paramIndex), param);
 742         } else if (mode != null && mode != WebParam.Mode.IN) {
 743             builder.processError(WebserviceapMessages.WEBSERVICEAP_NON_IN_PARAMETERS_MUST_BE_HOLDER(typeElement.getQualifiedName(), method.toString(), paramIndex), param);
 744         }
 745 
 746         return true;
 747     }
 748 
 749     protected boolean isDocLitWrapped() {
 750         return soapStyle.equals(SOAPStyle.DOCUMENT) && wrapped;
 751     }
 752 
 753     private static final class NoTypeVisitor extends SimpleTypeVisitor6<Boolean, Void> {
 754 
 755         @Override
 756         public Boolean visitNoType(NoType t, Void o) {
 757             return true;
 758         }
 759 
 760         @Override
 761         protected Boolean defaultAction(TypeMirror e, Void aVoid) {
 762             return false;
 763         }
 764     }
 765 
 766     protected boolean isValidOneWayMethod(ExecutableElement method, TypeElement typeElement) {
 767         boolean valid = true;
 768         if (!(method.getReturnType().accept(NO_TYPE_VISITOR, null))) {
 769             // this is an error, cannot be OneWay and have a return type
 770             builder.processError(WebserviceapMessages.WEBSERVICEAP_ONEWAY_OPERATION_CANNOT_HAVE_RETURN_TYPE(typeElement.getQualifiedName(), method.toString()), method);
 771             valid = false;
 772         }
 773         VariableElement outParam = getOutParameter(method);
 774         if (outParam != null) {
 775             builder.processError(WebserviceapMessages.WEBSERVICEAP_ONEWAY_AND_OUT(typeElement.getQualifiedName(), method.toString()), outParam);
 776             valid = false;
 777         }
 778         if (!isDocLitWrapped() && soapStyle.equals(SOAPStyle.DOCUMENT)) {
 779             int inCnt = getModeParameterCount(method, WebParam.Mode.IN);
 780             if (inCnt != 1) {
 781                 builder.processError(WebserviceapMessages.WEBSERVICEAP_ONEWAY_AND_NOT_ONE_IN(typeElement.getQualifiedName(), method.toString()), method);
 782                 valid = false;
 783             }
 784         }
 785         for (TypeMirror thrownType : method.getThrownTypes()) {
 786             TypeElement thrownElement = (TypeElement) ((DeclaredType) thrownType).asElement();
 787             if (builder.isServiceException(thrownType)) {
 788                 builder.processError(WebserviceapMessages.WEBSERVICEAP_ONEWAY_OPERATION_CANNOT_DECLARE_EXCEPTIONS(
 789                         typeElement.getQualifiedName(), method.toString(), thrownElement.getQualifiedName()), method);
 790                 valid = false;
 791             }
 792         }
 793         return valid;
 794     }
 795 
 796     protected int getModeParameterCount(ExecutableElement method, WebParam.Mode mode) {
 797         WebParam webParam;
 798         int cnt = 0;
 799         for (VariableElement param : method.getParameters()) {
 800             webParam = param.getAnnotation(WebParam.class);
 801             if (webParam != null) {
 802                 if (webParam.header())
 803                     continue;
 804                 if (isEquivalentModes(mode, webParam.mode()))
 805                     cnt++;
 806             } else {
 807                 if (isEquivalentModes(mode, WebParam.Mode.IN)) {
 808                     cnt++;
 809                 }
 810             }
 811         }
 812         return cnt;
 813     }
 814 
 815     protected boolean isEquivalentModes(WebParam.Mode mode1, WebParam.Mode mode2) {
 816         if (mode1.equals(mode2))
 817             return true;
 818         assert mode1 == WebParam.Mode.IN || mode1 == WebParam.Mode.OUT;
 819         return (mode1 == WebParam.Mode.IN && mode2 != WebParam.Mode.OUT) || (mode1 == WebParam.Mode.OUT && mode2 != WebParam.Mode.IN);
 820     }
 821 
 822     protected boolean isHolder(VariableElement param) {
 823         return builder.getHolderValueType(param.asType()) != null;
 824     }
 825 
 826     protected boolean isLegalType(TypeMirror type) {
 827         if (!(type != null && type.getKind().equals(TypeKind.DECLARED)))
 828             return true;
 829         TypeElement tE = (TypeElement) ((DeclaredType) type).asElement();
 830         if (tE == null) {
 831             // can be null, if this type's declaration is unknown. This may be the result of a processing error, such as a missing class file.
 832             builder.processError(WebserviceapMessages.WEBSERVICEAP_COULD_NOT_FIND_TYPEDECL(type.toString(), context.getRound()));
 833         }
 834         return !builder.isRemote(tE);
 835     }
 836 
 837     protected VariableElement getOutParameter(ExecutableElement method) {
 838         WebParam webParam;
 839         for (VariableElement param : method.getParameters()) {
 840             webParam = param.getAnnotation(WebParam.class);
 841             if (webParam != null && webParam.mode() != WebParam.Mode.IN) {
 842                 return param;
 843             }
 844         }
 845         return null;
 846     }
 847 
 848     protected static class MySoapBinding implements SOAPBinding {
 849 
 850         @Override
 851         public Style style() {
 852             return SOAPBinding.Style.DOCUMENT;
 853         }
 854 
 855         @Override
 856         public Use use() {
 857             return SOAPBinding.Use.LITERAL;
 858         }
 859 
 860         @Override
 861         public ParameterStyle parameterStyle() {
 862             return SOAPBinding.ParameterStyle.WRAPPED;
 863         }
 864 
 865         @Override
 866         public Class<? extends java.lang.annotation.Annotation> annotationType() {
 867             return SOAPBinding.class;
 868         }
 869     }
 870 }