KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > axis > description > JavaServiceDesc


1 /*
2  * Copyright 2002-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.axis.description;
17
18 import org.apache.axis.AxisServiceConfig;
19 import org.apache.axis.Constants;
20 import org.apache.axis.InternalException;
21 import org.apache.axis.AxisProperties;
22 import org.apache.axis.components.logger.LogFactory;
23 import org.apache.axis.encoding.*;
24 import org.apache.axis.constants.Style;
25 import org.apache.axis.constants.Use;
26 import org.apache.axis.message.SOAPBodyElement;
27 import org.apache.axis.message.SOAPEnvelope;
28 import org.apache.axis.utils.JavaUtils;
29 import org.apache.axis.utils.Messages;
30 import org.apache.axis.utils.bytecode.ParamNameExtractor;
31 import org.apache.axis.wsdl.Skeleton;
32 import org.apache.axis.wsdl.fromJava.Namespaces;
33 import org.apache.commons.logging.Log;
34 import org.w3c.dom.Document JavaDoc;
35 import org.w3c.dom.Element JavaDoc;
36
37 import javax.xml.namespace.QName JavaDoc;
38 import javax.xml.rpc.holders.Holder JavaDoc;
39 import java.lang.reflect.InvocationTargetException JavaDoc;
40 import java.lang.reflect.Method JavaDoc;
41 import java.lang.reflect.Modifier JavaDoc;
42 import java.util.ArrayList JavaDoc;
43 import java.util.Collection JavaDoc;
44 import java.util.Collections JavaDoc;
45 import java.util.Comparator JavaDoc;
46 import java.util.HashMap JavaDoc;
47 import java.util.Iterator JavaDoc;
48 import java.util.List JavaDoc;
49 import java.util.StringTokenizer JavaDoc;
50
51
52 /**
53  * A ServiceDesc is an abstract description of a service.
54  *
55  * ServiceDescs contain OperationDescs, which are descriptions of operations.
56  * The information about a service's operations comes from one of two places:
57  * 1) deployment, or 2) introspection.
58  *
59  * @author Glen Daniels (gdaniels@apache.org)
60  */

61 public class JavaServiceDesc implements ServiceDesc {
62     protected static Log log =
63             LogFactory.getLog(JavaServiceDesc.class.getName());
64
65     /** The name of this service */
66     private String JavaDoc name = null;
67
68     /** The documentation of this service */
69     private String JavaDoc documentation = null;
70
71     /** Style/Use */
72     private Style style = Style.RPC;
73     private Use use = Use.ENCODED;
74
75     // Style and Use are related. By default, if Style==RPC, Use should be
76
// ENCODED. But if Style==DOCUMENT, Use should be LITERAL. So we want
77
// to keep the defaults synced until someone explicitly sets the Use.
78
private boolean useSet = false;
79
80     /** Our operations - a list of OperationDescs */
81     private ArrayList JavaDoc operations = new ArrayList JavaDoc();
82
83     /** A collection of namespaces which will map to this service */
84     private List JavaDoc namespaceMappings = null;
85
86     /**
87      * Where does our WSDL document live? If this is non-null, the "?WSDL"
88      * generation will automatically return this file instead of dynamically
89      * creating a WSDL. BE CAREFUL because this means that Handlers will
90      * not be able to add to the WSDL for extensions/headers....
91      */

92     private String JavaDoc wsdlFileName = null;
93
94     /**
95      * An endpoint URL which someone has specified for this service. If
96      * this is set, WSDL generation will pick it up instead of defaulting
97      * to the transport URL.
98      */

99     private String JavaDoc endpointURL = null;
100
101     /** Place to store user-extensible service-related properties */
102     private HashMap JavaDoc properties = null;
103
104     /** Lookup caches */
105     private HashMap JavaDoc name2OperationsMap = null;
106     private HashMap JavaDoc qname2OperationsMap = null;
107     private transient HashMap JavaDoc method2OperationMap = new HashMap JavaDoc();
108     
109     // THE FOLLOWING STUFF IS ALL JAVA-SPECIFIC, AND WILL BE FACTORED INTO
110
// A JAVA-SPECIFIC SUBCLASS. --Glen
111

112     /** List of allowed methods */
113     /** null allows everything, an empty ArrayList allows nothing */
114     private List JavaDoc allowedMethods = null;
115
116     /** List if disallowed methods */
117     private List JavaDoc disallowedMethods = null;
118
119     /** Implementation class */
120     private Class JavaDoc implClass = null;
121
122     /**
123      * Is the implementation a Skeleton? If this is true, it will generate
124      * a Fault to provide OperationDescs via WSDD.
125      */

126     private boolean isSkeletonClass = false;
127
128     /** Cached copy of the skeleton "getOperationDescByName" method */
129     private transient Method JavaDoc skelMethod = null;
130
131     /** Classes at which we should stop looking up the inheritance chain
132      * when introspecting
133      */

134     private ArrayList JavaDoc stopClasses = null;
135
136     /** Lookup caches */
137     private transient HashMap JavaDoc method2ParamsMap = new HashMap JavaDoc();
138     private OperationDesc messageServiceDefaultOp = null;
139
140     /** Method names for which we have completed any introspection necessary */
141     private ArrayList JavaDoc completedNames = new ArrayList JavaDoc();
142
143     /** Our typemapping for resolving Java<->XML type issues */
144     private TypeMapping tm = null;
145     private TypeMappingRegistry tmr = null;
146
147     private boolean haveAllSkeletonMethods = false;
148     private boolean introspectionComplete = false;
149
150     /**
151      * Default constructor
152      */

153     public JavaServiceDesc() {
154     }
155
156     /**
157      * What kind of service is this?
158      * @return
159      */

160     public Style getStyle() {
161         return style;
162     }
163
164     public void setStyle(Style style) {
165         this.style = style;
166         if (!useSet) {
167             // Use hasn't been explicitly set, so track style
168
use = style == Style.RPC ? Use.ENCODED : Use.LITERAL;
169         }
170     }
171
172
173     /**
174      * What kind of use is this?
175      * @return
176      */

177     public Use getUse() {
178         return use;
179     }
180
181     public void setUse(Use use) {
182         useSet = true;
183         this.use = use;
184     }
185
186     /**
187      * Determine whether or not this is a "wrapped" invocation, i.e. whether
188      * the outermost XML element of the "main" body element represents a
189      * method call, with the immediate children of that element representing
190      * arguments to the method.
191      *
192      * @return true if this is wrapped (i.e. RPC or WRAPPED style),
193      * false otherwise
194      */

195     public boolean isWrapped()
196     {
197         return ((style == Style.RPC) ||
198                 (style == Style.WRAPPED));
199     }
200
201     /**
202      * the wsdl file of the service.
203      * When null, it means that the wsdl should be autogenerated
204      * @return filename or null
205      */

206     public String JavaDoc getWSDLFile() {
207         return wsdlFileName;
208     }
209
210     /**
211      * set the wsdl file of the service; this causes the named
212      * file to be returned on a ?wsdl, probe, not introspection
213      * generated wsdl.
214      * @param wsdlFileName filename or null to re-enable introspection
215      */

216     public void setWSDLFile(String JavaDoc wsdlFileName) {
217         this.wsdlFileName = wsdlFileName;
218     }
219
220     public List JavaDoc getAllowedMethods() {
221         return allowedMethods;
222     }
223
224     public void setAllowedMethods(List JavaDoc allowedMethods) {
225         this.allowedMethods = allowedMethods;
226     }
227
228     public Class JavaDoc getImplClass() {
229         return implClass;
230     }
231
232     /**
233      * set the implementation class
234      * <p>
235      * Warning: You cannot call getInitializedServiceDesc() after setting this
236      * as it uses this to indicate its work has already been done.
237      *
238      * @param implClass
239      * @throws IllegalArgumentException if the implementation class is already
240      * set
241      */

242     public void setImplClass(Class JavaDoc implClass) {
243         if (this.implClass != null)
244             throw new IllegalArgumentException JavaDoc(
245                     Messages.getMessage("implAlreadySet"));
246
247         this.implClass = implClass;
248         if (Skeleton.class.isAssignableFrom(implClass)) {
249             isSkeletonClass = true;
250             loadSkeletonOperations();
251         }
252     }
253
254     private void loadSkeletonOperations() {
255         Method JavaDoc method = null;
256         try {
257             method = implClass.getDeclaredMethod("getOperationDescs",
258                                                  new Class JavaDoc [] {});
259         } catch (NoSuchMethodException JavaDoc e) {
260         } catch (SecurityException JavaDoc e) {
261         }
262         if (method == null) {
263             // FIXME : Throw an error?
264
return;
265         }
266
267         try {
268             Collection JavaDoc opers = (Collection JavaDoc)method.invoke(implClass, null);
269             for (Iterator JavaDoc i = opers.iterator(); i.hasNext();) {
270                 OperationDesc skelDesc = (OperationDesc)i.next();
271                 addOperationDesc(skelDesc);
272             }
273         } catch (IllegalAccessException JavaDoc e) {
274             if(log.isDebugEnabled()) {
275                 log.debug(Messages.getMessage("exception00"), e);
276             }
277             return;
278         } catch (IllegalArgumentException JavaDoc e) {
279             if(log.isDebugEnabled()) {
280                 log.debug(Messages.getMessage("exception00"), e);
281             }
282             return;
283         } catch (InvocationTargetException JavaDoc e) {
284             if(log.isDebugEnabled()) {
285                 log.debug(Messages.getMessage("exception00"), e);
286             }
287             return;
288         }
289         haveAllSkeletonMethods = true;
290     }
291
292     public TypeMapping getTypeMapping() {
293         if(tm == null) {
294             return DefaultTypeMappingImpl.getSingletonDelegate();
295 // throw new RuntimeException(Messages.getMessage("noDefaultTypeMapping00"));
296
}
297         return tm;
298     }
299
300     public void setTypeMapping(TypeMapping tm) {
301         this.tm = tm;
302     }
303
304     /**
305      * the name of the service
306      */

307     public String JavaDoc getName() {
308         return name;
309     }
310
311     /**
312      * the name of the service
313      * @param name
314      */

315     public void setName(String JavaDoc name) {
316         this.name = name;
317     }
318
319     /**
320      * get the documentation for the service
321      */

322     public String JavaDoc getDocumentation() {
323         return documentation;
324     }
325
326     /**
327      * set the documentation for the service
328      */

329     public void setDocumentation(String JavaDoc documentation) {
330         this.documentation = documentation;
331     }
332
333     public ArrayList JavaDoc getStopClasses() {
334         return stopClasses;
335     }
336
337     public void setStopClasses(ArrayList JavaDoc stopClasses) {
338         this.stopClasses = stopClasses;
339     }
340
341     public List JavaDoc getDisallowedMethods() {
342         return disallowedMethods;
343     }
344
345     public void setDisallowedMethods(List JavaDoc disallowedMethods) {
346         this.disallowedMethods = disallowedMethods;
347     }
348
349     public void removeOperationDesc(OperationDesc operation) {
350         operations.remove(operation);
351         operation.setParent(null);
352
353         if (name2OperationsMap != null) {
354             String JavaDoc name = operation.getName();
355             ArrayList JavaDoc overloads = (ArrayList JavaDoc)name2OperationsMap.get(name);
356             if (overloads != null) {
357                 overloads.remove(operation);
358                 if (overloads.size() == 0) {
359                     name2OperationsMap.remove(name);
360                 }
361             }
362         }
363         
364         if (qname2OperationsMap != null) {
365             QName JavaDoc qname = operation.getElementQName();
366             ArrayList JavaDoc list = (ArrayList JavaDoc)qname2OperationsMap.get(qname);
367             if (list != null) {
368                 list.remove(operation);
369             }
370         }
371         
372         if (method2OperationMap != null) {
373             Method JavaDoc method = operation.getMethod();
374             if (method != null) {
375                 method2OperationMap.remove(method);
376             }
377         }
378     }
379     
380     public void addOperationDesc(OperationDesc operation)
381     {
382         operations.add(operation);
383         operation.setParent(this);
384         if (name2OperationsMap == null) {
385             name2OperationsMap = new HashMap JavaDoc();
386         }
387
388         // Add name to name2Operations Map
389
String JavaDoc name = operation.getName();
390         ArrayList JavaDoc overloads = (ArrayList JavaDoc)name2OperationsMap.get(name);
391         if (overloads == null) {
392             overloads = new ArrayList JavaDoc();
393             name2OperationsMap.put(name, overloads);
394         } else if (JavaUtils.isTrue(
395                 AxisProperties.getProperty(Constants.WSIBP11_COMPAT_PROPERTY)) &&
396                 overloads.size() > 0) {
397             throw new RuntimeException JavaDoc(Messages.getMessage("noOverloadedOperations", name));
398         }
399         overloads.add(operation);
400     }
401
402     /**
403      * get all the operations as a list of OperationDescs.
404      * this method triggers an evaluation of the valid operations by
405      * introspection, so use sparingly
406      * @return reference to the operations array. This is not a copy
407      */

408     public ArrayList JavaDoc getOperations()
409     {
410         loadServiceDescByIntrospection(); // Just in case...
411
return operations;
412     }
413
414     /**
415      * get all overloaded operations by name
416      * @param methodName
417      * @return null for no match, or an array of OperationDesc objects
418      */

419     public OperationDesc [] getOperationsByName(String JavaDoc methodName)
420     {
421         getSyncedOperationsForName(implClass, methodName);
422
423         if (name2OperationsMap == null)
424             return null;
425
426         ArrayList JavaDoc overloads = (ArrayList JavaDoc)name2OperationsMap.get(methodName);
427         if (overloads == null) {
428             return null;
429         }
430
431         OperationDesc [] array = new OperationDesc [overloads.size()];
432         return (OperationDesc[])overloads.toArray(array);
433     }
434
435     /**
436      * Return an operation matching the given method name. Note that if we
437      * have multiple overloads for this method, we will return the first one.
438      * @return null for no match
439      */

440     public OperationDesc getOperationByName(String JavaDoc methodName)
441     {
442         // If we need to load up operations from introspection data, do it.
443
// This returns fast if we don't need to do anything, so it's not very
444
// expensive.
445
getSyncedOperationsForName(implClass, methodName);
446
447         if (name2OperationsMap == null)
448             return null;
449
450         ArrayList JavaDoc overloads = (ArrayList JavaDoc)name2OperationsMap.get(methodName);
451         if (overloads == null) {
452             return null;
453         }
454
455         return (OperationDesc)overloads.get(0);
456     }
457
458     /**
459      * Map an XML QName to an operation. Returns the first one it finds
460      * in the case of mulitple matches.
461      * @return null for no match
462      */

463     public OperationDesc getOperationByElementQName(QName JavaDoc qname)
464     {
465         OperationDesc [] overloads = getOperationsByQName(qname);
466
467         // Return the first one....
468
if ((overloads != null) && overloads.length > 0)
469             return overloads[0];
470
471         return null;
472     }
473
474     /**
475      * Return all operations which match this QName (i.e. get all the
476      * overloads)
477      * @return null for no match
478      */

479     public OperationDesc [] getOperationsByQName(QName JavaDoc qname)
480     {
481         // Look in our mapping of QNames -> operations.
482

483         // But first, let's make sure we've initialized said mapping....
484
initQNameMap();
485
486         ArrayList JavaDoc overloads = (ArrayList JavaDoc)qname2OperationsMap.get(qname);
487         if (overloads == null) {
488             // Nothing specifically matching this QName.
489
if (name2OperationsMap != null) {
490                 if ((isWrapped() ||
491                      ((style == Style.MESSAGE) &&
492                       (getDefaultNamespace() == null)))) {
493                     // Try ignoring the namespace....?
494
overloads = (ArrayList JavaDoc) name2OperationsMap.get(qname.getLocalPart());
495                 } else {
496                     // TODO the above code is weird: a JavaServiceDesc can be document or rpc and
497
// still define a WSDL operation using a wrapper style mapping.
498
// The following code handles this case.
499
Object JavaDoc ops = name2OperationsMap.get(qname.getLocalPart());
500                     if (ops != null) {
501                         overloads = new ArrayList JavaDoc((Collection JavaDoc) ops);
502                         for (Iterator JavaDoc iter = overloads.iterator(); iter.hasNext();) {
503                             OperationDesc operationDesc = (OperationDesc) iter.next();
504                             if (Style.WRAPPED != operationDesc.getStyle()) {
505                                 iter.remove();
506                             }
507                         }
508                     }
509                 }
510             }
511             // Handle the case where a single Message-style operation wants
512
// to accept anything.
513
if ((style == Style.MESSAGE) && (messageServiceDefaultOp != null))
514                 return new OperationDesc [] { messageServiceDefaultOp };
515
516             if (overloads == null)
517                 return null;
518         }
519
520         getSyncedOperationsForName(implClass,
521                                    ((OperationDesc)overloads.get(0)).getName());
522
523         // Sort the overloads by number of arguments - prevents us calling methods
524
// with more parameters than supplied in the request (with missing parameters
525
// defaulted to null) when a perfectly good method exists with exactly the
526
// supplied parameters.
527
Collections.sort(overloads,
528             new Comparator JavaDoc() {
529                 public int compare(Object JavaDoc o1, Object JavaDoc o2)
530                 {
531                     Method JavaDoc meth1 = ((OperationDesc)o1).getMethod();
532                     Method JavaDoc meth2 = ((OperationDesc)o2).getMethod();
533                     return (meth1.getParameterTypes().length -
534                                          meth2.getParameterTypes().length);
535                 }
536             });
537
538         OperationDesc [] array = new OperationDesc [overloads.size()];
539         return (OperationDesc[])overloads.toArray(array);
540     }
541
542     private synchronized void initQNameMap() {
543         if (qname2OperationsMap == null) {
544             loadServiceDescByIntrospection();
545
546             qname2OperationsMap = new HashMap JavaDoc();
547             for (Iterator JavaDoc i = operations.iterator(); i.hasNext();) {
548                 OperationDesc operationDesc = (OperationDesc) i.next();
549                 QName JavaDoc qname = operationDesc.getElementQName();
550                 ArrayList JavaDoc list = (ArrayList JavaDoc)qname2OperationsMap.get(qname);
551                 if (list == null) {
552                     list = new ArrayList JavaDoc();
553                     qname2OperationsMap.put(qname, list);
554                 }
555                 list.add(operationDesc);
556             }
557         }
558     }
559
560     /**
561      * Synchronize an existing OperationDesc to a java.lang.Method.
562      *
563      * This method is used when the deployer has specified operation metadata
564      * and we want to match that up with a real java Method so that the
565      * Operation-level dispatch carries us all the way to the implementation.
566      * Search the declared methods on the implementation class to find one
567      * with an argument list which matches our parameter list.
568      */

569     private void syncOperationToClass(OperationDesc oper, Class JavaDoc implClass)
570     {
571         // ------------------------------------------------
572
// Developer Note:
573
//
574
// The goal of the sync code is to associate
575
// the OperationDesc/ParamterDesc with the
576
// target Method. There are a number of ways to get to this
577
// point depending on what information
578
// is available. Here are the main scenarios:
579
//
580
// A) Deployment with wsdd (non-skeleton):
581
// * OperationDesc/ParameterDesc loaded from deploy.wsdd
582
// * Loaded ParameterDesc does not have javaType,
583
// so it is discovered using the TypeMappingRegistry
584
// (also loaded via deploy.wsdd) and the
585
// typeQName specified by the ParameterDesc.
586
// * Sync occurs using the discovered
587
// javaTypes and the javaTypes of the Method
588
// parameters
589
//
590
// B) Deployment with no wsdd OperationDesc info (non-skeleton):
591
// * Implementation Class introspected to build
592
// OperationDesc/ParameterDesc.
593
// * ParameterDesc is known via introspection.
594
// * ParameterDesc are discovered using javaType
595
// and TypeMappingRegistry.
596
// * Sync occurs using the introspected
597
// javaTypes and the javaTypes of the Method
598
// parameters
599
//
600
// C) Deployment with wsdd (skeleton):
601
// * OperationDesc/ParameterDesc loaded from the Skeleton
602
// * In this scenario the ParameterDescs' already
603
// have javaTypes (see E below).
604
// * Sync occurs using the ParameterDesc
605
// javaTypes and the javaTypes of the Method
606
// parameters.
607
//
608
// D) Commandline Java2WSDL loading non-Skeleton Class/Interface
609
// * Class/Interface introspected to build
610
// OperationDesc/ParameterDesc.
611
// * The javaTypes of the ParameterDesc are set using introspection.
612
// * typeQNames are determined for built-in types using
613
// from the default TypeMappingRegistry. Other
614
// typeQNames are guessed from the javaType. Note
615
// that there is no loaded TypeMappingRegistry.
616
// * Sync occurs using the ParameterDesc
617
// javaTypes and the javaTypes of the Method
618
// parameters.
619
//
620
// E) Commandline Java2WSDL loading Skeleton Class
621
// * OperationDesc/ParameterDesc loaded from Skeleton
622
// * Each ParameterDesc has an appropriate typeQName
623
// * Each ParameterDesc also has a javaType, which is
624
// essential for sync'ing up with the
625
// method since there is no loaded TypeMappingRegistry.
626
// * Syncronization occurs using the ParameterDesc
627
// javaTypes and the javaTypes of the Method
628
// parameters.
629
//
630
// So in each scenario, the ultimate sync'ing occurs
631
// using the javaTypes of the ParameterDescs and the
632
// javaTypes of the Method parameters.
633
//
634
// ------------------------------------------------
635

636         // If we're already mapped to a Java method, no need to do anything.
637
if (oper.getMethod() != null)
638             return;
639
640         // Find the method. We do this once for each Operation.
641

642         Method JavaDoc[] methods = getMethods(implClass);
643         // A place to keep track of possible matches
644
Method JavaDoc possibleMatch = null;
645         
646         for (int i = 0; i < methods.length; i++) {
647             Method JavaDoc method = methods[i];
648             if (Modifier.isPublic(method.getModifiers()) &&
649                     method.getName().equals(oper.getName()) &&
650                     method2OperationMap.get(method) == null) {
651
652                 if (style == Style.MESSAGE) {
653                     int messageOperType = checkMessageMethod(method);
654                     if(messageOperType == OperationDesc.MSG_METHOD_NONCONFORMING) continue;
655                     if (messageOperType == -1) {
656                         throw new InternalException("Couldn't match method to any of the allowable message-style patterns!");
657                     }
658                     oper.setMessageOperationStyle(messageOperType);
659
660                     // Don't bother checking params if we're message style
661
possibleMatch = method;
662                     break;
663                 }
664
665                 // Check params
666
Class JavaDoc [] paramTypes = method.getParameterTypes();
667                 if (paramTypes.length != oper.getNumParams())
668                     continue;
669
670                 int j;
671                 boolean conversionNecessary = false;
672                 for (j = 0; j < paramTypes.length; j++) {
673                     Class JavaDoc type = paramTypes[j];
674                     Class JavaDoc actualType = type;
675                     if (Holder JavaDoc.class.isAssignableFrom(type)) {
676                         actualType = JavaUtils.getHolderValueType(type);
677                     }
678                     ParameterDesc param = oper.getParameter(j);
679                     QName JavaDoc typeQName = param.getTypeQName();
680                     if (typeQName == null) {
681                         // No typeQName is available. Set it using
682
// information from the actual type.
683
// (Scenarios B and D)
684
// There is no need to try and match with
685
// the Method parameter javaType because
686
// the ParameterDesc is being constructed
687
// by introspecting the Method.
688
typeQName = getTypeMapping().getTypeQName(actualType);
689                         param.setTypeQName(typeQName);
690                     } else {
691                         // A type qname is available.
692
// Ensure that the ParameterDesc javaType
693
// is convertable to the Method parameter type
694
//
695
// Use the available javaType (Scenarios C and E)
696
// or get one from the TMR (Scenario A).
697
Class JavaDoc paramClass = param.getJavaType();
698                         if (paramClass != null &&
699                             JavaUtils.getHolderValueType(paramClass) != null) {
700                             paramClass = JavaUtils.getHolderValueType(paramClass);
701                         }
702                         if (paramClass == null) {
703                             paramClass = getTypeMapping().getClassForQName(param.getTypeQName(),
704                                                                            type);
705                         }
706
707                         if (paramClass != null) {
708                             // This is a match if the paramClass is somehow
709
// convertable to the "real" parameter type. If not,
710
// break out of this loop.
711
if (!JavaUtils.isConvertable(paramClass, actualType)) {
712                                 break;
713                             }
714                             
715                             if (!actualType.isAssignableFrom(paramClass)) {
716                                 // This doesn't fit without conversion
717
conversionNecessary = true;
718                             }
719                         }
720                     }
721                     // In all scenarios the ParameterDesc javaType is set to
722
// match the javaType in the corresponding parameter.
723
// This is essential.
724
param.setJavaType(type);
725                 }
726
727                 if (j != paramTypes.length) {
728                     // failed.
729
continue;
730                 }
731                 
732                 // This is our latest possibility
733
possibleMatch = method;
734
735                 // If this is exactly it, stop now. Otherwise keep looking
736
// just in case we find a better match.
737
if (!conversionNecessary) {
738                     break;
739                 }
740
741             }
742         }
743
744         // At this point, we may or may not have a possible match.
745
// FIXME : Should we prefer an exact match from a base class over
746
// a with-conversion match from the target class? If so,
747
// we'll need to change the logic below.
748
if (possibleMatch != null) {
749             Class JavaDoc returnClass = possibleMatch.getReturnType();
750             oper.setReturnClass(returnClass);
751             
752             QName JavaDoc returnType = oper.getReturnType();
753             if (returnType == null) {
754                 oper.setReturnType(getTypeMapping().getTypeQName(returnClass));
755             }
756
757             // Do the faults
758
createFaultMetadata(possibleMatch, oper);
759                 
760             oper.setMethod(possibleMatch);
761             method2OperationMap.put(possibleMatch, oper);
762             return;
763         }
764
765         // Didn't find a match. Try the superclass, if appropriate
766
Class JavaDoc superClass = implClass.getSuperclass();
767         if (superClass != null &&
768                 !superClass.getName().startsWith("java.") &&
769                 !superClass.getName().startsWith("javax.") &&
770                 (stopClasses == null ||
771                           !stopClasses.contains(superClass.getName()))) {
772             syncOperationToClass(oper, superClass);
773         }
774
775         // Exception if sync fails to find method for operation
776
if (oper.getMethod() == null) {
777             InternalException ie =
778                 new InternalException(Messages.getMessage("serviceDescOperSync00",
779                                                            oper.getName(),
780                                                            implClass.getName()));
781             throw ie;
782         }
783     }
784
785     private Method