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