View Javadoc

    1   package org.codehaus.xfire.service.binding;
    2
    3   import java.lang.reflect.Method;
    4   import java.lang.reflect.Modifier;
    5   import java.net.URL;
    6   import java.util.HashSet;
    7   import java.util.Iterator;
    8   import java.util.Map;
    9   import java.util.Set;
    10
    11  import javax.wsdl.factory.WSDLFactory;
    12  import javax.wsdl.xml.WSDLReader;
    13  import javax.xml.namespace.QName;
    14
    15  import org.codehaus.xfire.MessageContext;
    16  import org.codehaus.xfire.XFireRuntimeException;
    17  import org.codehaus.xfire.fault.FaultSender;
    18  import org.codehaus.xfire.fault.Soap11FaultSerializer;
    19  import org.codehaus.xfire.fault.Soap12FaultSerializer;
    20  import org.codehaus.xfire.handler.OutMessageSender;
    21  import org.codehaus.xfire.service.MessageInfo;
    22  import org.codehaus.xfire.service.OperationInfo;
    23  import org.codehaus.xfire.service.Service;
    24  import org.codehaus.xfire.service.ServiceFactory;
    25  import org.codehaus.xfire.service.ServiceInfo;
    26  import org.codehaus.xfire.soap.Soap11;
    27  import org.codehaus.xfire.soap.SoapConstants;
    28  import org.codehaus.xfire.soap.SoapVersion;
    29  import org.codehaus.xfire.transport.TransportManager;
    30  import org.codehaus.xfire.util.ClassLoaderUtils;
    31  import org.codehaus.xfire.util.NamespaceHelper;
    32  import org.codehaus.xfire.util.ServiceUtils;
    33  import org.codehaus.xfire.wsdl.ResourceWSDL;
    34  import org.codehaus.xfire.wsdl11.WSDL11ParameterBinding;
    35  import org.codehaus.xfire.wsdl11.builder.WSDLBuilderAdapter;
    36  import org.codehaus.xfire.wsdl11.builder.DefaultWSDLBuilderFactory;
    37  import org.codehaus.xfire.wsdl11.builder.WSDLBuilderFactory;
    38
    39  /***
    40   * Java objects-specific implementation of the {@link ServiceFactory} interface.
    41   *
    42   * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
    43   * @author <a href="mailto:poutsma@mac.com">Arjen Poutsma</a>
    44   */
    45  public class ObjectServiceFactory
    46          implements ServiceFactory
    47  {
    48      private BindingProvider bindingProvider;
    49      private TransportManager transportManager;
    50      private String style;
    51      private String use;
    52      private Set ignoredClasses = new HashSet();
    53      private SoapVersion soapVersion = Soap11.getInstance();
    54      private boolean voidOneWay;
    55      private WSDLBuilderFactory wsdlBuilderFactory = new DefaultWSDLBuilderFactory();
    56
    57      /***
    58       * Initializes a new instance of the <code>ObjectServiceFactory</code>.
    59       */
    60      public ObjectServiceFactory()
    61      {
    62          setStyle(SoapConstants.STYLE_WRAPPED);
    63          setUse(SoapConstants.USE_LITERAL);
    64          ignoredClasses.add("java.lang.Object");
    65          ignoredClasses.add("org.omg.CORBA_2_3.portable.ObjectImpl");
    66          ignoredClasses.add("org.omg.CORBA.portable.ObjectImpl");
    67          ignoredClasses.add("javax.ejb.EJBObject");
    68          ignoredClasses.add("javax.rmi.CORBA.Stub");
    69      }
    70
    71      /***
    72       * Initializes a new instance of the <code>ObjectServiceFactory</code> with the given transport manager and type
    73       * mapping registry.
    74       *
    75       * @param transportManager the transport manager
    76       * @param provider         the binding provider
    77       */
    78      public ObjectServiceFactory(TransportManager transportManager, BindingProvider provider)
    79      {
    80          this();
    81
    82          this.bindingProvider = provider;
    83          this.transportManager = transportManager;
    84      }
    85
    86      public ObjectServiceFactory(TransportManager transportManager)
    87      {
    88          this();
    89
    90          this.transportManager = transportManager;
    91      }
    92
    93      public BindingProvider getBindingProvider()
    94      {
    95          if (bindingProvider == null)
    96          {
    97              try
    98              {
    99                  bindingProvider = (BindingProvider) ClassLoaderUtils
    100                         .loadClass("org.codehaus.xfire.aegis.AegisBindingProvider", getClass()).newInstance();
    101             }
    102             catch (Exception e)
    103             {
    104                 throw new XFireRuntimeException("Couldn't find a binding provider!", e);
    105             }
    106         }
    107
    108         return bindingProvider;
    109     }
    110
    111     /***
    112      * @param wsdlUrl
    113      * @return
    114      */
    115     public Service create(Class clazz, URL wsdlUrl)
    116             throws Exception
    117     {
    118         WSDLReader reader = WSDLFactory.newInstance().newWSDLReader();
    119         reader.readWSDL(wsdlUrl.toString());
    120
    121         QName name = ServiceUtils.makeQualifiedNameFromClass(clazz);
    122         ServiceInfo serviceInfo = new ServiceInfo(name, clazz);
    123         Service endpoint = new Service(serviceInfo);
    124
    125         endpoint.setWSDLWriter(new ResourceWSDL(wsdlUrl));
    126
    127         // TODO: Bring wsdl configuration functionality back!
    128
    129         throw new UnsupportedOperationException("create() isn't working yet.");
    130
    131         // return endpoint;
    132     }
    133
    134     /***
    135      * Creates a service from the specified class. The service name will be the
    136      * unqualified class name. The namespace will be based on the package.
    137      * The service will use soap version 1.1, wrapped style, and literal use.
    138      *
    139      * @param clazz
    140      *            The service class used to populate the operations and
    141      *            parameters. If the class is an interface, then the
    142      *            implementation class that implements that interface must be
    143      *            set via {@link Service#setProperty(String, Object)} with the
    144      *            property key being
    145      *            {@link org.codehaus.xfire.service.binding.ObjectInvoker#SERVICE_IMPL_CLASS}
    146      * @return The service.
    147      */
    148     public Service create(Class clazz)
    149     {
    150         return create(clazz, (Map) null);
    151     }
    152
    153     /***
    154      * Creates a service from the specified class. The service name will be the
    155      * unqualified class name. The namespace will be based on the package.
    156      * The service will use soap version 1.1, wrapped style, and literal use.
    157      *
    158      * @param clazz
    159      *            The service class used to populate the operations and
    160      *            parameters. If the class is an interface, then the
    161      *            implementation class that implements that interface must be
    162      *            set via {@link Service#setProperty(String, Object)} with the
    163      *            property key being
    164      *            {@link org.codehaus.xfire.service.binding.ObjectInvoker#SERVICE_IMPL_CLASS}
    165      * @return The service.
    166      */
    167     public Service create(Class clazz, Map properties)
    168     {
    169         return create(clazz, null, null, properties);
    170     }
    171     /***
    172      * Creates a service from the specified class, soap version, style and use. The returned service will have a name
    173      * based on the class name, and a namespace based on the class package.
    174      * <p/>
    175      * Some parameters can be <code>null</code>, and will be replaced with sensible defaults if so. See the specific
    176      * parameters for more info.
    177      *
    178      * @param clazz            The service class used to populate the operations and parameters.
    179      * @param name             The name of the service. If <code>null</code>, a name will be generated from the class
    180      *                         name.
    181      * @param namespace        The default namespace of the service. If <code>null</code>, a namespace will be generated
    182      *                         from the class package.
    183      * @return The service.
    184      */
    185     public Service create(Class clazz, String name, String namespace, Map properties)
    186     {
    187         return create(clazz, name, namespace, null, null, null, properties);
    188     }
    189
    190     protected String makeServiceNameFromClassName(Class clazz)
    191     {
    192         return ServiceUtils.makeServiceNameFromClassName(clazz);
    193     }
    194
    195     public Service create(Class clazz,
    196                           String name,
    197                           String namespace,
    198                           SoapVersion version,
    199                           String style,
    200                           String use,
    201                           Map properties)
    202     {
    203         String theName = (name != null) ? name : makeServiceNameFromClassName(clazz);
    204         String theNamespace = (namespace != null) ? namespace : NamespaceHelper.makeNamespaceFromClassName(
    205                 clazz.getName(), "http");
    206         QName qName = new QName(theNamespace, theName);
    207         SoapVersion theVersion = (version != null) ? version : soapVersion;
    208         String theStyle = (style != null) ? style : this.style;
    209         String theUse = (use != null) ? use : this.use;
    210
    211         ServiceInfo serviceInfo = new ServiceInfo(qName, clazz);
    212
    213         Service endpoint = new Service(serviceInfo);
    214         setProperties(endpoint, properties);
    215         endpoint.setSoapVersion(theVersion);
    216
    217         ObjectBinding binding = ObjectBindingFactory.getMessageBinding(theStyle, theUse);
    218         binding.setInvoker(new ObjectInvoker());
    219         endpoint.setBinding(binding);
    220
    221         if (theVersion instanceof Soap11)
    222         {
    223             endpoint.setFaultSerializer(new Soap11FaultSerializer());
    224         }
    225         else
    226         {
    227             endpoint.setFaultSerializer(new Soap12FaultSerializer());
    228         }
    229
    230         if (transportManager != null && binding instanceof WSDL11ParameterBinding)
    231         {
    232             endpoint.setWSDLWriter(new WSDLBuilderAdapter(getWsdlBuilderFactory(),
    233                                                           endpoint,
    234                                                           transportManager,
    235                                                           (WSDL11ParameterBinding) binding));
    236         }
    237
    238         initializeOperations(endpoint);
    239
    240         try
    241         {
    242             BindingProvider provider = getBindingProvider();
    243             provider.initialize(endpoint);
    244             binding.setBindingProvider(provider);
    245         }
    246         catch (Exception e)
    247         {
    248             if(e instanceof XFireRuntimeException) throw (XFireRuntimeException)e;
    249             throw new XFireRuntimeException("Couldn't load provider.", e);
    250         }
    251
    252         registerHandlers(endpoint);
    253
    254         return endpoint;
    255     }
    256
    257     protected void registerHandlers(Service service)
    258     {
    259         service.addOutHandler(new OutMessageSender());
    260         service.addInHandler(service.getBinding());
    261         service.addFaultHandler(new FaultSender());
    262     }
    263
    264     private void setProperties(Service service, Map properties)
    265     {
    266         if (properties == null) return;
    267
    268         for (Iterator itr = properties.entrySet().iterator(); itr.hasNext();)
    269         {
    270             Map.Entry entry = (Map.Entry) itr.next();
    271
    272             service.setProperty((String) entry.getKey(), entry.getValue());
    273         }
    274     }
    275
    276     protected void initializeOperations(Service endpoint)
    277     {
    278         final Method[] methods = endpoint.getServiceInfo().getServiceClass().getMethods();
    279
    280         for (int i = 0; i < methods.length; i++)
    281         {
    282             final Method method = methods[i];
    283
    284             if (isValidMethod(method))
    285             {
    286                 addOperation(endpoint, method);
    287             }
    288         }
    289     }
    290
    291     /***
    292      * Ignore the specified class' declared methods.
    293      * This can be used to not expose certain interfaces as a service.
    294      * By default, the methods specified by the following interfaces/classes are ignored:
    295      * <li><code>java.lang.Object</code>
    296      * <li><code>org.omg.CORBA_2_3.portable.ObjectImpl</code>
    297      * <li><code>org.omg.CORBA.portable.ObjectImpl</code>
    298      * <li><code>javax.ejb.EJBObject</code>
    299      * <li><code>javax.ejb.EJBLocalObject</code>
    300      * <li><code>javax.rmi.CORBA.Stub</code>
    301      *
    302      * @param className the fully qualified class name
    303      */
    304     public void addIgnoredMethods(String className)
    305     {
    306         ignoredClasses.add(className);
    307     }
    308
    309     protected boolean isValidMethod(final Method method)
    310     {
    311         if(ignoredClasses.contains(method.getDeclaringClass().getName())) return false;
    312
    313         final int modifiers = method.getModifiers();
    314
    315         return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers);
    316     }
    317
    318     protected void addOperation(Service endpoint, final Method method)
    319     {
    320         ServiceInfo service = endpoint.getServiceInfo();
    321         AbstractBinding binding = (AbstractBinding) endpoint.getBinding();
    322
    323         final String opName = getOperationName(service, method);
    324
    325         final OperationInfo op = service.addOperation(opName, method);
    326
    327         op.setAction(getAction(op));
    328
    329         final Class[] paramClasses = method.getParameterTypes();
    330
    331         final boolean isDoc = binding.getStyle().equals(SoapConstants.STYLE_DOCUMENT);
    332
    333         MessageInfo inMsg = op.createMessage(new QName(op.getName() + "Request"));
    334         op.setInputMessage(inMsg);
    335
    336         for (int j = 0; j < paramClasses.length; j++)
    337         {
    338             if (isHeader(method, j))
    339             {
    340                 final QName q = getInParameterName(endpoint, method, j, isDoc);
    341                 inMsg.addMessageHeader(q, paramClasses[j]).setIndex(j);
    342             }
    343             else if (!paramClasses[j].equals(MessageContext.class))
    344             {
    345                 final QName q = getInParameterName(endpoint, method, j, isDoc);
    346                 inMsg.addMessagePart(q, paramClasses[j]).setIndex(j);
    347             }
    348         }
    349
    350         MessageInfo outMsg = op.createMessage(new QName(op.getName() + "Response"));
    351         op.setOutputMessage(outMsg);
    352
    353         final Class returnType = method.getReturnType();
    354         if (!returnType.isAssignableFrom(void.class))
    355         {
    356             if (isHeader(method, -1))
    357             {
    358                 final QName q =  getOutParameterName(endpoint, method, isDoc);
    359                 outMsg.addMessageHeader(q, method.getReturnType()).setIndex(0);
    360             }
    361             else
    362             {
    363                 final QName q = getOutParameterName(endpoint, method, isDoc);
    364                 outMsg.addMessagePart(q, method.getReturnType());
    365             }
    366         }
    367
    368         op.setMEP(getMEP(method));
    369         op.setAsync(isAsync(method));
    370     }
    371
    372     protected String getAction(OperationInfo op)
    373     {
    374         return "";
    375     }
    376
    377     protected boolean isHeader(Method method, int j)
    378     {
    379         return false;
    380     }
    381
    382     /***
    383      * Creates a name for the operation from the method name. If an operation with that name
    384      * already exists, a name is create by appending an integer to the end. I.e. if there is already
    385      * two methods named <code>doSomething</code>, the first one will have an operation name of
    386      * "doSomething" and the second "doSomething1".
    387      *
    388      * @param service
    389      * @param method
    390      * @return
    391      */
    392     protected String getOperationName(ServiceInfo service, Method method)
    393     {
    394         if (service.getOperation(method.getName()) == null)
    395         {
    396             return method.getName();
    397         }
    398
    399         int i = 1;
    400         while (true)
    401         {
    402             String name = method.getName() + i;
    403             if (service.getOperation(name) == null)
    404             {
    405                 return name;
    406             }
    407             else
    408             {
    409                 i++;
    410             }
    411         }
    412     }
    413
    414     protected String getMEP(final Method method)
    415     {
    416         if (isVoidOneWay() && method.getReturnType().equals(void.class))
    417         {
    418             return SoapConstants.MEP_IN;
    419         }
    420         return SoapConstants.MEP_ROBUST_IN_OUT;
    421     }
    422
    423     protected boolean isAsync(final Method method)
    424     {
    425         return false;
    426     }
    427
    428     protected QName getInParameterName(Service endpoint,
    429                                        final Method method,
    430                                        final int paramNumber,
    431                                        final boolean doc)
    432     {
    433         String paramName = "";
    434         if (doc)
    435             paramName = method.getName();
    436
    437         return new QName(endpoint.getServiceInfo().getName().getNamespaceURI(), paramName + "in" + paramNumber);
    438     }
    439
    440     protected QName getOutParameterName(Service endpoint, final Method method, final boolean doc)
    441     {
    442         String outName = "";
    443         if (doc)
    444             outName = method.getName();
    445
    446         return new QName(endpoint.getServiceInfo().getName().getNamespaceURI(), outName + "out");
    447     }
    448
    449     public TransportManager getTransportManager()
    450     {
    451         return transportManager;
    452     }
    453
    454     public void setTransportManager(TransportManager transportManager)
    455     {
    456         this.transportManager = transportManager;
    457     }
    458
    459     public void setBindingProvider(BindingProvider bindingProvider)
    460     {
    461         this.bindingProvider = bindingProvider;
    462     }
    463
    464     public String getStyle()
    465     {
    466         return style;
    467     }
    468
    469     public void setStyle(String style)
    470     {
    471         this.style = style;
    472     }
    473
    474     public String getUse()
    475     {
    476         return use;
    477     }
    478
    479     public void setUse(String use)
    480     {
    481         this.use = use;
    482     }
    483
    484     public SoapVersion getSoapVersion()
    485     {
    486         return soapVersion;
    487     }
    488
    489     public void setSoapVersion(SoapVersion soapVersion)
    490     {
    491         this.soapVersion = soapVersion;
    492     }
    493
    494     public boolean isVoidOneWay()
    495     {
    496         return voidOneWay;
    497     }
    498
    499     public void setVoidOneWay(boolean voidOneWay)
    500     {
    501         this.voidOneWay = voidOneWay;
    502     }
    503
    504     public WSDLBuilderFactory getWsdlBuilderFactory()
    505     {
    506         return wsdlBuilderFactory;
    507     }
    508
    509     public void setWsdlBuilderFactory(WSDLBuilderFactory wsdlBuilderFactory)
    510     {
    511         this.wsdlBuilderFactory = wsdlBuilderFactory;
    512     }
    513 }