Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/axis/providers/java/EJBProvider.java


1   /*
2    * Copyright 2001-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  
17  package org.apache.axis.providers.java;
18  
19  import java.lang.reflect.Method;
20  import java.lang.reflect.InvocationTargetException;
21  import java.util.Properties;
22  
23  import javax.naming.Context;
24  import javax.naming.InitialContext;
25  
26  import org.apache.axis.AxisFault;
27  import org.apache.axis.Constants;
28  import org.apache.axis.Handler;
29  import org.apache.axis.MessageContext;
30  import org.apache.axis.components.logger.LogFactory;
31  import org.apache.axis.handlers.soap.SOAPService;
32  import org.apache.axis.utils.ClassUtils;
33  import org.apache.axis.utils.Messages;
34  import org.apache.commons.logging.Log;
35  
36  /**
37   * A basic EJB Provider
38   *
39   * @author Carl Woolf (cwoolf@macromedia.com)
40   * @author Tom Jordahl (tomj@macromedia.com)
41   * @author C?dric Chabanois (cchabanois@ifrance.com)
42   */
43  public class EJBProvider extends RPCProvider
44  {
45      protected static Log log =
46          LogFactory.getLog(EJBProvider.class.getName());
47  
48      // The enterprise category is for stuff that an enterprise product might
49      // want to track, but in a simple environment (like the AXIS build) would
50      // be nothing more than a nuisance.
51      protected static Log entLog =
52          LogFactory.getLog(Constants.ENTERPRISE_LOG_CATEGORY);
53  
54      public static final String OPTION_BEANNAME = "beanJndiName";
55      public static final String OPTION_HOMEINTERFACENAME = "homeInterfaceName";
56      public static final String OPTION_REMOTEINTERFACENAME = "remoteInterfaceName";
57      public static final String OPTION_LOCALHOMEINTERFACENAME = "localHomeInterfaceName";
58      public static final String OPTION_LOCALINTERFACENAME = "localInterfaceName";
59      
60      
61      public static final String jndiContextClass = "jndiContextClass";
62      public static final String jndiURL = "jndiURL";
63      public static final String jndiUsername = "jndiUser";
64      public static final String jndiPassword = "jndiPassword";
65      
66      protected static final Class[] empty_class_array = new Class[0];
67      protected static final Object[] empty_object_array = new Object[0];
68      
69      private static InitialContext cached_context = null;
70  
71      ///////////////////////////////////////////////////////////////
72      ///////////////////////////////////////////////////////////////
73      /////// Default methods from JavaProvider ancestor, overridden
74      ///////   for ejbeans
75      ///////////////////////////////////////////////////////////////
76      ///////////////////////////////////////////////////////////////
77  
78      /**
79       * Return a object which implements the service.
80       * 
81       * @param msgContext the message context
82       * @param clsName The JNDI name of the EJB home class
83       * @return an object that implements the service
84       */
85      protected Object makeNewServiceObject(MessageContext msgContext,
86                                             String clsName)
87          throws Exception
88      {
89          String remoteHomeName = getStrOption(OPTION_HOMEINTERFACENAME, 
90                                                  msgContext.getService());
91          String localHomeName = getStrOption(OPTION_LOCALHOMEINTERFACENAME, 
92                                                  msgContext.getService());
93          String homeName = (remoteHomeName != null ? remoteHomeName:localHomeName);
94  
95          if (homeName == null) {
96              // cannot find both remote home and local home  
97              throw new AxisFault(
98                  Messages.getMessage("noOption00", 
99                                      OPTION_HOMEINTERFACENAME, 
100                                     msgContext.getTargetService()));
101         }
102                                                         
103         // Load the Home class name given in the config file
104         Class homeClass = ClassUtils.forName(homeName, true, msgContext.getClassLoader());
105 
106         // we create either the ejb using either the RemoteHome or LocalHome object
107         if (remoteHomeName != null)
108             return createRemoteEJB(msgContext, clsName, homeClass);
109         else 
110             return createLocalEJB(msgContext, clsName, homeClass);
111     }
112 
113     /**
114      * Create an EJB using a remote home object
115      * 
116      * @param msgContext the message context
117      * @param beanJndiName The JNDI name of the EJB remote home class
118      * @param homeClass the class of the home interface
119      * @return an EJB
120      */
121     private Object createRemoteEJB(MessageContext msgContext, 
122                                     String beanJndiName,
123                                     Class homeClass)
124         throws Exception
125     {
126         // Get the EJB Home object from JNDI 
127         Object ejbHome = getEJBHome(msgContext.getService(),
128                                     msgContext, beanJndiName);
129         Object ehome = javax.rmi.PortableRemoteObject.narrow(ejbHome, homeClass);
130 
131         // Invoke the create method of the ejbHome class without actually
132         // touching any EJB classes (i.e. no cast to EJBHome)
133         Method createMethod = homeClass.getMethod("create", empty_class_array);
134         Object result = createMethod.invoke(ehome, empty_object_array);
135         
136         return result;        
137     }
138 
139     /**
140      * Create an EJB using a local home object
141      * 
142      * @param msgContext the message context
143      * @param beanJndiName The JNDI name of the EJB local home class
144      * @param homeClass the class of the home interface
145      * @return an EJB
146      */
147     private Object createLocalEJB(MessageContext msgContext, 
148                                    String beanJndiName,
149                                    Class homeClass)
150         throws Exception
151     {
152         // Get the EJB Home object from JNDI 
153         Object ejbHome = getEJBHome(msgContext.getService(),
154                                     msgContext, beanJndiName);
155 
156         // the home object is a local home object
157         Object ehome;
158         if (homeClass.isInstance(ejbHome))
159           ehome = ejbHome;
160         else
161           throw new ClassCastException(
162                   Messages.getMessage("badEjbHomeType"));
163           
164         // Invoke the create method of the ejbHome class without actually
165         // touching any EJB classes (i.e. no cast to EJBLocalHome)
166         Method createMethod = homeClass.getMethod("create", empty_class_array);
167         Object result = createMethod.invoke(ehome, empty_object_array);
168                                
169         return result;
170     }
171 
172     /**
173      * Tells if the ejb that will be used to handle this service is a remote
174      * one
175      */
176     private boolean isRemoteEjb(SOAPService service)
177     {
178         return getStrOption(OPTION_HOMEINTERFACENAME,service) != null; 
179     }
180 
181     /**
182      * Tells if the ejb that will be used to handle this service is a local
183      * one
184      */
185     private boolean isLocalEjb(SOAPService service)
186     {
187         return (!isRemoteEjb(service)) && 
188           (getStrOption(OPTION_LOCALHOMEINTERFACENAME,service) != null);
189     }
190 
191 
192     /**
193      * Return the option in the configuration that contains the service class
194      * name.  In the EJB case, it is the JNDI name of the bean.
195      */
196     protected String getServiceClassNameOptionName()
197     {
198         return OPTION_BEANNAME;
199     }
200 
201     /**
202      * Get a String option by looking first in the service options,
203      * and then at the Handler's options.  This allows defaults to be
204      * specified at the provider level, and then overriden for particular
205      * services.
206      *
207      * @param optionName the option to retrieve
208      * @return String the value of the option or null if not found in
209      *                either scope
210      */
211     protected String getStrOption(String optionName, Handler service)
212     {
213         String value = null;
214         if (service != null)
215             value = (String)service.getOption(optionName);
216         if (value == null)
217             value = (String)getOption(optionName);
218         return value;
219     }
220 
221     /**
222      * Get the remote interface of an ejb from its home class.
223      * This function can only be used for remote ejbs
224      * 
225      * @param beanJndiName the jndi name of the ejb
226      * @param service the soap service
227      * @param msgContext the message context (can be null)
228      */
229     private Class getRemoteInterfaceClassFromHome(String beanJndiName,
230                                                    SOAPService service,
231                                                    MessageContext msgContext)
232         throws Exception                                       
233     {
234         // Get the EJB Home object from JNDI
235         Object ejbHome = getEJBHome(service, msgContext, beanJndiName);
236 
237         String homeName = getStrOption(OPTION_HOMEINTERFACENAME,
238                                        service);
239         if (homeName == null)
240             throw new AxisFault(
241                     Messages.getMessage("noOption00",
242                                         OPTION_HOMEINTERFACENAME,
243                                         service.getName()));
244 
245         // Load the Home class name given in the config file
246         ClassLoader cl = (msgContext != null) ?
247                 msgContext.getClassLoader() :
248                 Thread.currentThread().getContextClassLoader();
249         Class homeClass = ClassUtils.forName(homeName, true, cl);
250 
251 
252         // Make sure the object we got back from JNDI is the same type
253         // as the what is specified in the config file
254         Object ehome = javax.rmi.PortableRemoteObject.narrow(ejbHome, homeClass);
255 
256         // This code requires the use of ejb.jar, so we do the stuff below
257         //   EJBHome ejbHome = (EJBHome) ehome;
258         //   EJBMetaData meta = ejbHome.getEJBMetaData();
259         //   Class interfaceClass = meta.getRemoteInterfaceClass();
260 
261         // Invoke the getEJBMetaData method of the ejbHome class without
262         // actually touching any EJB classes (i.e. no cast to EJBHome)
263         Method getEJBMetaData =
264                 homeClass.getMethod("getEJBMetaData", empty_class_array);
265         Object metaData = getEJBMetaData.invoke(ehome, empty_object_array);
266         Method getRemoteInterfaceClass =
267                 metaData.getClass().getMethod("getRemoteInterfaceClass",
268                                                   empty_class_array);
269         return (Class) getRemoteInterfaceClass.invoke(metaData,
270                                                        empty_object_array);
271     }                                      
272 
273     
274     /**
275      * Get the class description for the EJB Remote or Local Interface, 
276      * which is what we are interested in exposing to the world (i.e. in WSDL).
277      * 
278      * @param msgContext the message context (can be null)
279      * @param beanJndiName the JNDI name of the EJB
280      * @return the class info of the EJB remote or local interface
281      */ 
282     protected Class getServiceClass(String beanJndiName,
283                                     SOAPService service,
284                                     MessageContext msgContext)
285         throws AxisFault
286     {
287         Class interfaceClass = null;
288         
289         try {
290             // First try to get the interface class from the configuation
291             // Note that we don't verify that remote remoteInterfaceName is used for
292             // remote ejb and localInterfaceName for local ejb. Should we ?
293             String remoteInterfaceName = 
294                     getStrOption(OPTION_REMOTEINTERFACENAME, service);
295             String localInterfaceName = 
296                     getStrOption(OPTION_LOCALINTERFACENAME, service);
297             String interfaceName = (remoteInterfaceName != null ? remoteInterfaceName : localInterfaceName);
298 
299             if(interfaceName != null){
300                 ClassLoader cl = (msgContext != null) ?
301                         msgContext.getClassLoader() :
302                         Thread.currentThread().getContextClassLoader();
303                 interfaceClass = ClassUtils.forName(interfaceName,
304                                                     true,
305                                                     cl);
306             } 
307             else
308             {
309                 // cannot get the interface name from the configuration, we get
310                 // it from the EJB Home (if remote)
311                 if (isRemoteEjb(service)) {
312                     interfaceClass = getRemoteInterfaceClassFromHome(beanJndiName,
313                                                                      service,
314                                                                      msgContext);
315                 }
316                 else 
317                 if (isLocalEjb(service)) {
318                     // we cannot get the local interface from the local ejb home
319                     // localInterfaceName is mandatory for local ejbs
320                     throw new AxisFault(
321                             Messages.getMessage("noOption00", 
322                                                 OPTION_LOCALINTERFACENAME, 
323                                                 service.getName()));
324                 }
325                 else
326                 {
327                     // neither a local ejb or a remote one ...
328                     throw new AxisFault(Messages.getMessage("noOption00", 
329                                                 OPTION_HOMEINTERFACENAME,
330                                                 service.getName()));
331                 }
332             }
333         } catch (Exception e) {
334             throw AxisFault.makeFault(e);
335         }
336 
337         // got it, return it
338        return interfaceClass;
339     }
340 
341     /**
342      * Common routine to do the JNDI lookup on the Home interface object
343      * username and password for jndi lookup are got from the configuration or from
344      * the messageContext if not found in the configuration
345      */ 
346     private Object getEJBHome(SOAPService serviceHandler,
347                               MessageContext msgContext,
348                               String beanJndiName)
349         throws AxisFault
350     {
351         Object ejbHome = null;
352         
353         // Set up an InitialContext and use it get the beanJndiName from JNDI
354         try {
355             Properties properties = null;
356 
357             // collect all the properties we need to access JNDI:
358             // username, password, factoryclass, contextUrl
359 
360             // username
361             String username = getStrOption(jndiUsername, serviceHandler);
362             if ((username == null) && (msgContext != null))
363                username = msgContext.getUsername();
364             if (username != null) {
365                 if (properties == null)
366                     properties = new Properties();
367                 properties.setProperty(Context.SECURITY_PRINCIPAL, username);
368             }
369 
370             // password
371             String password = getStrOption(jndiPassword, serviceHandler);
372             if ((password == null) && (msgContext != null))
373                 password = msgContext.getPassword();
374             if (password != null) {
375                 if (properties == null)
376                     properties = new Properties();
377                 properties.setProperty(Context.SECURITY_CREDENTIALS, password);
378             }
379 
380             // factory class
381             String factoryClass = getStrOption(jndiContextClass, serviceHandler);
382             if (factoryClass != null) {
383                 if (properties == null)
384                     properties = new Properties();
385                 properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, factoryClass);
386             }
387 
388             // contextUrl
389             String contextUrl = getStrOption(jndiURL, serviceHandler);
390             if (contextUrl != null) {
391                 if (properties == null)
392                     properties = new Properties();
393                 properties.setProperty(Context.PROVIDER_URL, contextUrl);
394             }
395 
396             // get context using these properties 
397             InitialContext context = getContext(properties);
398 
399             // if we didn't get a context, fail
400             if (context == null)
401                 throw new AxisFault( Messages.getMessage("cannotCreateInitialContext00"));
402             
403             ejbHome = getEJBHome(context, beanJndiName);
404 
405             if (ejbHome == null)
406                 throw new AxisFault( Messages.getMessage("cannotFindJNDIHome00",beanJndiName));
407         }
408         // Should probably catch javax.naming.NameNotFoundException here 
409         catch (Exception exception) {
410             entLog.info(Messages.getMessage("toAxisFault00"), exception);
411             throw AxisFault.makeFault(exception);
412         }
413 
414         return ejbHome;
415     }
416     
417     protected InitialContext getCachedContext()
418         throws javax.naming.NamingException
419     {
420         if (cached_context == null)
421             cached_context = new InitialContext();
422         return cached_context;
423     }
424         
425 
426     protected InitialContext getContext(Properties properties)
427         throws AxisFault, javax.naming.NamingException
428     {
429         // if we got any stuff from the configuration file
430         // create a new context using these properties 
431         // otherwise, we get a default context and cache it for next time
432         return ((properties == null)
433                 ? getCachedContext()
434                 : new InitialContext(properties));
435     }
436 
437     protected Object getEJBHome(InitialContext context, String beanJndiName)
438         throws AxisFault, javax.naming.NamingException
439     {
440         // Do the JNDI lookup
441         return context.lookup(beanJndiName);
442     }
443 
444     /**
445      * Override the default implementation such that we can include
446      * special handling for {@link java.rmi.ServerException}.
447      * <p/>
448      * Converts {@link java.rmi.ServerException} exceptions to
449      * {@link InvocationTargetException} exceptions with the same cause.
450      * This allows the axis framework to create a SOAP fault.
451      * </p>
452      *
453      * @see org.apache.axis.providers.java.RPCProvider#invokeMethod(org.apache.axis.MessageContext, java.lang.reflect.Method, java.lang.Object, java.lang.Object[])
454      */
455     protected Object invokeMethod(MessageContext msgContext, Method method,
456                                   Object obj, Object[] argValues)
457             throws Exception {
458         try {
459             return super.invokeMethod(msgContext, method, obj, argValues);
460         } catch (InvocationTargetException ite) {
461             Throwable cause = getCause(ite);
462             if (cause instanceof java.rmi.ServerException) {
463                 throw new InvocationTargetException(getCause(cause));
464             }
465             throw ite;
466         }
467     }
468 
469     /**
470      * Get the cause of an exception, using reflection so that
471      * it still works under JDK 1.3
472      *
473      * @param original the original exception
474      * @return the cause of the exception, or the given exception if the cause cannot be discovered.
475      */
476     private Throwable getCause(Throwable original) {
477         try {
478             Method method = original.getClass().getMethod("getCause", null);
479             Throwable cause = (Throwable) method.invoke(original, null);
480             if (cause != null) {
481                 return cause;
482             }
483         } catch (NoSuchMethodException nsme) {
484             // ignore, this occurs under JDK 1.3 
485         } catch (Throwable t) {
486         }
487         return original;
488     }
489 }