KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > axis > description > ServiceDesc


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 2002-2003 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Axis" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation. For more
52  * information on the Apache Software Foundation, please see
53  * <http://www.apache.org/>.
54  */

55 package org.jboss.axis.description;
56
57 import org.jboss.axis.AxisServiceConfig;
58 import org.jboss.axis.Constants;
59 import org.jboss.axis.InternalException;
60 import org.jboss.axis.encoding.DefaultTypeMappingImpl;
61 import org.jboss.axis.encoding.TypeMapping;
62 import org.jboss.axis.encoding.TypeMappingRegistry;
63 import org.jboss.axis.encoding.TypeMappingRegistryImpl;
64 import org.jboss.axis.enums.Style;
65 import org.jboss.axis.enums.Use;
66 import org.jboss.axis.message.SOAPBodyElementAxisImpl;
67 import org.jboss.axis.message.SOAPEnvelopeAxisImpl;
68 import org.jboss.axis.utils.JavaUtils;
69 import org.jboss.axis.utils.Messages;
70 import org.jboss.axis.utils.bytecode.ParamNameExtractor;
71 import org.jboss.axis.wsdl.Skeleton;
72 import org.jboss.axis.wsdl.fromJava.Namespaces;
73 import org.jboss.logging.Logger;
74 import org.w3c.dom.Document JavaDoc;
75 import org.w3c.dom.Element JavaDoc;
76
77 import javax.xml.namespace.QName JavaDoc;
78 import javax.xml.rpc.holders.Holder JavaDoc;
79 import javax.xml.rpc.server.ServiceLifecycle JavaDoc;
80 import java.lang.reflect.InvocationTargetException JavaDoc;
81 import java.lang.reflect.Method JavaDoc;
82 import java.lang.reflect.Modifier JavaDoc;
83 import java.util.ArrayList JavaDoc;
84 import java.util.Collection JavaDoc;
85 import java.util.Collections JavaDoc;
86 import java.util.Comparator JavaDoc;
87 import java.util.HashMap JavaDoc;
88 import java.util.Iterator JavaDoc;
89 import java.util.List JavaDoc;
90 import java.util.StringTokenizer JavaDoc;
91
92
93 /**
94  * A ServiceDesc is an abstract description of a service.
95  * <p/>
96  * ServiceDescs contain OperationDescs, which are descriptions of operations.
97  * The information about a service's operations comes from one of two places:
98  * 1) deployment, or 2) introspection.
99  *
100  * @author Glen Daniels (gdaniels@apache.org)
101  */

102 public class ServiceDesc
103 {
104
105    private static Logger log = Logger.getLogger(ServiceDesc.class.getName());
106
107    /**
108     * The name of this service
109     */

110    private String JavaDoc name = null;
111
112    /** List of allowed methods */
113    /**
114     * null allows everything, an empty ArrayList allows nothing
115     */

116    private List JavaDoc allowedMethods = null;
117
118    /**
119     * List if disallowed methods
120     */

121    private List JavaDoc disallowedMethods = null;
122
123    /**
124     * Style/Use
125     */

126    private Style style = Style.RPC;
127    private Use use = Use.ENCODED;
128
129    // Style and Use are related. By default, if Style==RPC, Use should be
130
// ENCODED. But if Style==DOCUMENT, Use should be LITERAL. So we want
131
// to keep the defaults synced until someone explicitly sets the Use.
132
private boolean useSet = false;
133
134    /**
135     * Implementation class
136     */

137    private Class JavaDoc implClass = null;
138
139    /**
140     * Our operations - a list of OperationDescs
141     */

142    private ArrayList JavaDoc operations = new ArrayList JavaDoc();
143
144    /**
145     * A collection of namespaces which will map to this service
146     */

147    private List JavaDoc namespaceMappings = null;
148
149    /**
150     * Where does our WSDL document live? If this is non-null, the "?WSDL"
151     * generation will automatically return this file instead of dynamically
152     * creating a WSDL. BE CAREFUL because this means that Handlers will
153     * not be able to add to the WSDL for extensions/headers....
154     */

155    private String JavaDoc wsdlFileName = null;
156
157    /**
158     * An endpoint URL which someone has specified for this service. If
159     * this is set, WSDL generation will pick it up instead of defaulting
160     * to the transport URL.
161     */

162    private String JavaDoc endpointURL = null;
163
164    /**
165     * Place to store user-extensible service-related properties
166     */

167    private HashMap JavaDoc properties = null;
168
169    /**
170     * Is the implementation a Skeleton? If this is true, it will generate
171     * a Fault to provide OperationDescs via WSDD.
172     */

173    private boolean isSkeletonClass = false;
174
175    /**
176     * Cached copy of the skeleton "getOperationDescByName" method
177     */

178    private Method JavaDoc skelMethod = null;
179
180    /**
181     * Classes at which we should stop looking up the inheritance chain
182     * when introspecting
183     */

184    private ArrayList JavaDoc stopClasses = null;
185
186    /**
187     * Lookup caches
188     */

189    private HashMap JavaDoc name2OperationsMap = null;
190    private HashMap JavaDoc qname2OperationsMap = null;
191    private HashMap JavaDoc method2OperationMap = new HashMap JavaDoc();
192    private HashMap JavaDoc method2ParamsMap = new HashMap JavaDoc();
193    private OperationDesc messageServiceDefaultOp = null;
194
195    /**
196     * Method names for which we have completed any introspection necessary
197     */

198    private ArrayList JavaDoc completedNames = new ArrayList JavaDoc();
199
200    /**
201     * Our typemapping for resolving Java<->XML type issues
202     */

203    private TypeMapping tm = DefaultTypeMappingImpl.getSingleton();
204
205    private TypeMappingRegistry tmr = null;
206
207    private boolean haveAllSkeletonMethods = false;
208    private boolean introspectionComplete = false;
209
210    /**
211     * Default constructor
212     */

213    public ServiceDesc()
214    {
215    }
216
217    /**
218     * What kind of service is this?
219     *
220     * @return
221     */

222    public Style getStyle()
223    {
224       return style;
225    }
226
227    public void setStyle(Style style)
228    {
229       this.style = style;
230       if (!useSet)
231       {
232          // Use hasn't been explicitly set, so track style
233
use = style == Style.RPC ? Use.ENCODED : Use.LITERAL;
234       }
235    }
236
237    /**
238     * What kind of use is this?
239     *
240     * @return
241     */

242    public Use getUse()
243    {
244       return use;
245    }
246
247    public void setUse(Use use)
248    {
249       useSet = true;
250       this.use = use;
251    }
252
253    /**
254     * Determine whether or not this is a "wrapped" invocation, i.e. whether
255     * the outermost XML element of the "main" body element represents a
256     * method call, with the immediate children of that element representing
257     * arguments to the method.
258     *
259     * @return true if this is wrapped (i.e. RPC or WRAPPED style),
260     * false otherwise
261     */

262    public boolean isWrapped()
263    {
264       return ((style == Style.RPC) ||
265               (style == Style.WRAPPED));
266    }
267
268    /**
269     * the wsdl file of the service.
270     * When null, it means that the wsdl should be autogenerated
271     *
272     * @return filename or null
273     */

274    public String JavaDoc getWSDLFile()
275    {
276       return wsdlFileName;
277    }
278
279    /**
280     * set the wsdl file of the service; this causes the named
281     * file to be returned on a ?wsdl, probe, not introspection
282     * generated wsdl.
283     *
284     * @param wsdlFileName filename or null to re-enable introspection
285     */

286    public void setWSDLFile(String JavaDoc wsdlFileName)
287    {
288       this.wsdlFileName = wsdlFileName;
289    }
290
291    public List JavaDoc getAllowedMethods()
292    {
293       return allowedMethods;
294    }
295
296    public void setAllowedMethods(List JavaDoc allowedMethods)
297    {
298       this.allowedMethods = allowedMethods;
299    }
300
301    public Class JavaDoc getImplClass()
302    {
303       return implClass;
304    }
305
306    /**
307     * set the implementation class
308     * <p/>
309     * Warning: You cannot call getInitializedServiceDesc() after setting this
310     * as it uses this to indicate its work has already been done.
311     *
312     * @param implClass
313     * @throws IllegalArgumentException if the implementation class is already
314     * set
315     */

316    public void setImplClass(Class JavaDoc implClass)
317    {
318       if (this.implClass != null)
319          throw new IllegalArgumentException JavaDoc(Messages.getMessage("implAlreadySet"));
320
321       this.implClass = implClass;
322       if (Skeleton.class.isAssignableFrom(implClass))
323       {
324          isSkeletonClass = true;
325          loadSkeletonOperations();
326       }
327    }
328
329    private void loadSkeletonOperations()
330    {
331       Method JavaDoc method = null;
332       try
333       {
334          method = implClass.getDeclaredMethod("getOperationDescs",
335                  new Class JavaDoc[]{});
336       }
337       catch (NoSuchMethodException JavaDoc e)
338       {
339       }
340       catch (SecurityException JavaDoc e)
341       {
342       }
343       if (method == null)
344       {
345          // FIXME : Throw an error?
346
return;
347       }
348
349       try
350       {
351          Collection JavaDoc opers = (Collection JavaDoc)method.invoke(implClass, null);
352          for (Iterator JavaDoc i = opers.iterator(); i.hasNext();)
353          {
354             OperationDesc skelDesc = (OperationDesc)i.next();
355             addOperationDesc(skelDesc);
356          }
357       }
358       catch (IllegalAccessException JavaDoc e)
359       {
360          return;
361       }
362       catch (IllegalArgumentException JavaDoc e)
363       {
364          return;
365       }
366       catch (InvocationTargetException JavaDoc e)
367       {
368          return;
369       }
370       haveAllSkeletonMethods = true;
371    }
372
373    public TypeMapping getTypeMapping()
374    {
375       return tm;
376    }
377
378    public void setTypeMapping(TypeMapping tm)
379    {
380       this.tm = tm;
381    }
382
383    /**
384     * the name of the service
385     */

386    public String JavaDoc getName()
387    {
388       return name;
389    }
390
391    /**
392     * the name of the service
393     *
394     * @param name
395     */

396    public void setName(String JavaDoc name)
397    {
398       this.name = name;
399    }
400
401    public ArrayList JavaDoc getStopClasses()
402    {
403       return stopClasses;
404    }
405
406    public void setStopClasses(ArrayList JavaDoc stopClasses)
407    {
408       this.stopClasses = stopClasses;
409    }
410
411    public List JavaDoc getDisallowedMethods()
412    {
413       return disallowedMethods;
414    }
415
416    public void setDisallowedMethods(List JavaDoc disallowedMethods)
417    {
418       this.disallowedMethods = disallowedMethods;
419    }
420
421    public void addOperationDesc(OperationDesc operation)
422    {
423       operations.add(operation);
424       operation.setParent(this);
425       if (name2OperationsMap == null)
426       {
427          name2OperationsMap = new HashMap JavaDoc();
428       }
429
430       // Add name to name2Operations Map
431
String JavaDoc name = operation.getName();
432       ArrayList JavaDoc overloads = (ArrayList JavaDoc)name2OperationsMap.get(name);
433       if (overloads == null)
434       {
435          overloads = new ArrayList JavaDoc();
436          name2OperationsMap.put(name, overloads);
437       }
438       overloads.add(operation);
439    }
440
441    /**
442     * get all the operations as a list of OperationDescs.
443     * this method triggers an evaluation of the valid operations by
444     * introspection, so use sparingly
445     *
446     * @return reference to the operations array. This is not a copy
447     */

448    public ArrayList JavaDoc getOperations()
449    {
450       loadServiceDescByIntrospection(); // Just in case...
451
return operations;
452    }
453
454    /**
455     * get all overloaded operations by name
456     *
457     * @param methodName
458     * @return null for no match, or an array of OperationDesc objects
459     */

460    public OperationDesc[] getOperationsByName(String JavaDoc methodName)
461    {
462       getSyncedOperationsForName(implClass, methodName);
463
464       if (name2OperationsMap == null)
465          return null;
466
467       ArrayList JavaDoc overloads = (ArrayList JavaDoc)name2OperationsMap.get(methodName);
468       if (overloads == null)
469       {
470          return null;
471       }
472
473       OperationDesc[] array = new OperationDesc[overloads.size()];
474       return (OperationDesc[])overloads.toArray(array);
475    }
476
477    /**
478     * Return an operation matching the given method name. Note that if we
479     * have multiple overloads for this method, we will return the first one.
480     *
481     * @return null for no match
482     */

483    public OperationDesc getOperationByName(String JavaDoc methodName)
484    {
485       // If we need to load up operations from introspection data, do it.
486
// This returns fast if we don't need to do anything, so it's not very
487
// expensive.
488
getSyncedOperationsForName(implClass, methodName);
489
490       if (name2OperationsMap == null)
491          return null;
492
493       ArrayList JavaDoc overloads = (ArrayList JavaDoc)name2OperationsMap.get(methodName);
494       if (overloads == null)
495       {
496          return null;
497       }
498
499       return (OperationDesc)overloads.get(0);
500    }
501
502    /**
503     * Map an XML QName to an operation. Returns the first one it finds
504     * in the case of mulitple matches.
505     *
506     * @return null for no match
507     */

508    public OperationDesc getOperationByElementQName(QName JavaDoc qname)
509    {
510       OperationDesc[] overloads = getOperationsByQName(qname);
511
512       // Return the first one....
513
if ((overloads != null) && overloads.length > 0)
514          return overloads[0];
515
516       return null;
517    }
518
519    /**
520     * Return all operations which match this QName (i.e. get all the
521     * overloads)
522     *
523     * @return null for no match
524     */

525    public OperationDesc[] getOperationsByQName(QName JavaDoc qname)
526    {
527       // Look in our mapping of QNames -> operations.
528

529       // But first, let's make sure we've initialized said mapping....
530
initQNameMap();
531
532       ArrayList JavaDoc overloads = (ArrayList JavaDoc)qname2OperationsMap.get(qname);
533
534       if (overloads == null)
535       {
536          // Nothing specifically matching this QName.
537
if ((isWrapped() ||
538                  ((style == Style.MESSAGE) &&
539                  (getDefaultNamespace() == null))) &&
540                  (name2OperationsMap != null))
541          {
542             // Try ignoring the namespace....?
543
overloads = (ArrayList JavaDoc)name2OperationsMap.get(qname.getLocalPart());
544          }
545
546          // Handle the case where a single Message-style operation wants
547
// to accept anything.
548
if ((style == Style.MESSAGE) && (messageServiceDefaultOp != null))
549             return new OperationDesc[]{messageServiceDefaultOp};
550
551          if (overloads == null)
552             return null;
553       }
554
555       getSyncedOperationsForName(implClass,
556               ((OperationDesc)overloads.get(0)).getName());
557
558       // Sort the overloads by number of arguments - prevents us calling methods
559
// with more parameters than supplied in the request (with missing parameters
560
// defaulted to null) when a perfectly good method exists with exactly the
561
// supplied parameters.
562
Collections.sort(overloads,
563               new Comparator JavaDoc()
564               {
565                  public int compare(Object JavaDoc o1, Object JavaDoc o2)
566                  {
567                     Method JavaDoc meth1 = ((OperationDesc)o1).getMethod();
568                     Method JavaDoc meth2 = ((OperationDesc)o2).getMethod();
569                     return (meth1.getParameterTypes().length -
570                             meth2.getParameterTypes().length);
571                  }
572               });
573
574       OperationDesc[] array = new OperationDesc[overloads.size()];
575       return (OperationDesc[])overloads.toArray(array);
576    }
577
578    private synchronized void initQNameMap()
579    {
580       if (qname2OperationsMap == null)
581       {
582          loadServiceDescByIntrospection();
583
584          qname2OperationsMap = new HashMap JavaDoc();
585          for (Iterator JavaDoc i = operations.iterator(); i.hasNext();)
586          {
587             OperationDesc operationDesc = (OperationDesc)i.next();
588             QName JavaDoc qname = operationDesc.getElementQName();
589             ArrayList JavaDoc list = (ArrayList JavaDoc)qname2OperationsMap.get(qname);
590             if (list == null)
591             {
592                list = new ArrayList JavaDoc();
593                qname2OperationsMap.put(qname, list);
594             }
595             list.add(operationDesc);
596          }
597       }
598    }
599
600    /**
601     * Synchronize an existing OperationDesc to a java.lang.Method.
602     * <p/>
603     * This method is used when the deployer has specified operation metadata
604     * and we want to match that up with a real java Method so that the
605     * Operation-level dispatch carries us all the way to the implementation.
606     * Search the declared methods on the implementation class to find one
607     * with an argument list which matches our parameter list.
608     */

609    private void syncOperationToClass(OperationDesc opDesc, Class JavaDoc implClass)
610    {
611       log.debug("Enter: syncOperationToClass " + opDesc);
612
613       // ------------------------------------------------
614
// Developer Note:
615
//
616
// The goal of the sync code is to associate
617
// the OperationDesc/ParamterDesc with the
618
// target Method. There are a number of ways to get to this
619
// point depending on what information
620
// is available. Here are the main scenarios:
621
//
622
// A) Deployment with wsdd (non-skeleton):
623
// * OperationDesc/ParameterDesc loaded from deploy.wsdd
624
// * Loaded ParameterDesc does not have javaType,
625
// so it is discovered using the TypeMappingRegistry
626
// (also loaded via deploy.wsdd) and the
627
// typeQName specified by the ParameterDesc.
628
// * Sync occurs using the discovered
629
// javaTypes and the javaTypes of the Method
630
// parameters
631
//
632
// B) Deployment with no wsdd OperationDesc info (non-skeleton):
633
// * Implementation Class introspected to build
634
// OperationDesc/ParameterDesc.
635
// * ParameterDesc is known via introspection.
636
// * ParameterDesc are discovered using javaType
637
// and TypeMappingRegistry.
638
// * Sync occurs using the introspected
639
// javaTypes and the javaTypes of the Method
640
// parameters
641
//
642
// C) Deployment with wsdd (skeleton):
643
// * OperationDesc/ParameterDesc loaded from the Skeleton
644
// * In this scenario the ParameterDescs' already
645
// have javaTypes (see E below).
646
// * Sync occurs using the ParameterDesc
647
// javaTypes and the javaTypes of the Method
648
// parameters.
649
//
650
// D) Commandline Java2WSDL loading non-Skeleton Class/Interface
651
// * Class/Interface introspected to build
652
// OperationDesc/ParameterDesc.
653
// * The javaTypes of the ParameterDesc are set using introspection.
654
// * typeQNames are determined for built-in types using
655
// from the default TypeMappingRegistry. Other
656
// typeQNames are guessed from the javaType. Note
657
// that there is no loaded TypeMappingRegistry.
658
// * Sync occurs using the ParameterDesc
659
// javaTypes and the javaTypes of the Method
660
// parameters.
661
//
662
// E) Commandline Java2WSDL loading Skeleton Class
663
// * OperationDesc/ParameterDesc loaded from Skeleton
664
// * Each ParameterDesc has an appropriate typeQName
665
// * Each ParameterDesc also has a javaType, which is
666
// essential for sync'ing up with the
667
// method since there is no loaded TypeMappingRegistry.
668
// * Syncronization occurs using the ParameterDesc
669
// javaTypes and the javaTypes of the Method
670
// parameters.
671
//
672
// So in each scenario, the ultimate sync'ing occurs
673
// using the javaTypes of the ParameterDescs and the
674
// javaTypes of the Method parameters.
675
//
676
// ------------------------------------------------
677

678       // If we're already mapped to a Java method, no need to do anything.
679
if (opDesc.getMethod() != null)
680          return;
681
682       // Find the method. We do this once for each Operation.
683

684       Method JavaDoc[] methods = implClass.getDeclaredMethods();
685       // A place to keep track of possible matches
686
Method JavaDoc possibleMatch = null;
687
688       for (int i = 0; i < methods.length; i++)
689       {
690          Method JavaDoc method = methods[i];
691          if (Modifier.isPublic(method.getModifiers()) &&
692                  method.getName().equals(opDesc.getName()) &&
693                  method2OperationMap.get(method) == null)
694          {
695
696             log.debug("Sync method: " + method);
697
698             if (style == Style.MESSAGE)
699             {
700                int messageOperType = checkMessageMethod(method);
701                if (messageOperType == OperationDesc.MSG_METHOD_NONCONFORMING) continue;
702                if (messageOperType == -1)
703                {
704                   throw new InternalException("Couldn't match method to any of the allowable message-style patterns!");
705                }
706                opDesc.setMessageOperationStyle(messageOperType);
707             }
708
709             // Check params
710
Class JavaDoc[] paramTypes = method.getParameterTypes();
711             if (paramTypes.length != opDesc.getNumParams())
712             {
713                log.debug("Number of parameters don't match");
714                continue;
715             }
716
717             int j;
718             boolean conversionNecessary = false;
719             for (j = 0; j < paramTypes.length; j++)
720             {
721                Class JavaDoc type = paramTypes[j];
722                Class JavaDoc actualType = type;
723
724                log.debug("Converting param: " + type);
725
726                if (Holder JavaDoc.class.isAssignableFrom(type))
727                   actualType = JavaUtils.getHolderValueType(type);
728
729                ParameterDesc param = opDesc.getParameter(j);
730                Class JavaDoc paramClass = param.getJavaType();
731
732                QName JavaDoc typeQName = param.getTypeQName();
733                if (typeQName == null)
734                {
735                   // No typeQName is available. Set it using
736
// information from the actual type.
737
// (Scenarios B and D)
738
// There is no need to try and match with
739
// the Method parameter javaType because
740
// the ParameterDesc is being constructed
741
// by introspecting the Method.
742
typeQName = tm.getTypeQName(type);
743                   param.setTypeQName(typeQName);
744                   log.debug("Setting param TypeQName: " + typeQName);
745
746                }
747                else
748                {
749                   // A type qname is available.
750
// Ensure that the ParameterDesc javaType
751
// is convertable to the Method parameter type
752
//
753
// Use the available javaType (Scenarios C and E)
754
// or get one from the TMR (Scenario A).
755
if (paramClass != null && JavaUtils.getHolderValueType(paramClass) != null)
756                   {
757                      paramClass = JavaUtils.getHolderValueType(paramClass);
758                      log.debug("Setting param class to holder type: " + paramClass);
759                   }
760                   if (paramClass == null)
761                   {
762                      paramClass = tm.getClassForQName(param.getTypeQName());
763                      log.debug("Setting param class from TypeQName: " + paramClass);
764                   }
765
766                   if (paramClass != null)
767                   {
768                      // This is a match if the paramClass is somehow
769
// convertable to the "real" parameter type. If not,
770
// break out of this loop.
771
if (!JavaUtils.isConvertable(paramClass, actualType))
772                      {
773                         log.debug("Param class is not convertible: [param=" + paramClass + ",actual=" + actualType + "]");
774                         break;
775                      }
776
777                      if (!actualType.isAssignableFrom(paramClass))
778                      {
779                         // This doesn't fit without conversion
780
conversionNecessary = true;
781                         log.debug("Actual type is not assignable from param class: [param=" + paramClass + ",actual=" + actualType + "]");
782                      }
783                   }
784                }
785                // In all scenarios the ParameterDesc javaType is set to
786
// match the javaType in the corresponding parameter.
787
// This is essential.
788
param.setJavaType(type);
789                log.debug("Setting param java type: " + type);
790             }
791
792             if (j != paramTypes.length)
793             {
794                // failed.
795
continue;
796             }
797
798             // This is our latest possibility
799
possibleMatch = method;
800             log.debug("Possible match: " + possibleMatch);
801
802             // If this is exactly it, stop now. Otherwise keep looking
803
// just in case we find a better match.
804
if (!conversionNecessary)
805             {
806                break;
807             }
808
809             log.debug("Conversion still necessary");
810          }
811       }
812
813       // At this point, we may or may not have a possible match.
814
// FIXME : Should we prefer an exact match from a base class over
815
// a with-conversion match from the target class? If so,
816
// we'll need to change the logic below.
817
if (possibleMatch != null)
818       {
819          ParameterDesc retParamDesc = opDesc.getReturnParamDesc();
820          QName JavaDoc retTypeQName = retParamDesc.getTypeQName();
821          Class JavaDoc retType = retParamDesc.getJavaType();
822          if (retTypeQName != null && retType == null)
823             retType = tm.getClassForQName(retTypeQName);
824
825          if (retType != null)
826          {
827             Class JavaDoc seiReturnType = possibleMatch.getReturnType();
828             if (JavaUtils.isConvertable(retType, seiReturnType))
829             {
830                opDesc.setReturnClass(seiReturnType);
831                log.debug("Setting return type: " + seiReturnType);
832             }
833             else
834             {
835                log.warn("Return type is not convertible to: " + retType);
836                //possibleMatch = null;
837
}
838          }
839
840          if (possibleMatch != null)
841          {
842             // Do the faults
843
createFaultMetadata(possibleMatch, opDesc);
844
845             opDesc.setMethod(possibleMatch);
846             method2OperationMap.put(possibleMatch, opDesc);
847             log.debug("Setting operation method: " + possibleMatch);
848          }
849       }
850
851       // Didn't find a match. Try the superclass, if appropriate
852
if (opDesc.getMethod() == null)
853       {
854          Class JavaDoc superClass = implClass.getSuperclass();
855          if (superClass != null &&
856                  !superClass.getName().startsWith("java.") &&
857                  !superClass.getName().startsWith("javax.") &&
858                  (stopClasses == null ||
859                  !stopClasses.contains(superClass.getName())))
860          {
861
862             log.debug("No match found, trying super class");
863             syncOperationToClass(opDesc, superClass);
864          }
865       }
866
867       // Exception if sync fails to find method for operation
868
if (opDesc.getMethod() == null)
869       {
870          InternalException ie =
871                  new InternalException(Messages.getMessage("serviceDescOperSync00",
872                          opDesc.getName(),
873                          implClass.getName()));
874          throw ie;
875       }
876    }
877
878    private int checkMessageMethod(Method JavaDoc method)
879    {
880       // Collect the types so we know what we're dealing with in the target
881
// method.
882
Class JavaDoc[] params = method.getParameterTypes();
883
884       if (params.length == 1)
885       {
886          if ((params[0] == Element JavaDoc[].class) &&
887                  (method.getReturnType() == Element JavaDoc[].class))
888          {
889             return OperationDesc.MSG_METHOD_ELEMENTARRAY;
890          }
891
892          if ((params[0] == SOAPBodyElementAxisImpl[].class) &&
893                  (method.getReturnType() == SOAPBodyElementAxisImpl[].class))
894          {
895             return OperationDesc.MSG_METHOD_BODYARRAY;
896          }
897
898          if ((params[0] == Document JavaDoc.class) &&
899                  (method.getReturnType() == Document