001: /*
002: * (C) Copyright 2000 - 2005 Nabh Information Systems, Inc.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU General Public License
006: * as published by the Free Software Foundation; either version 2
007: * of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
017: *
018: */
019:
020: /*
021: * This file contains fragments from Apache Axis class.
022: *
023: * Copyright 2001-2004 The Apache Software Foundation.
024: *
025: * Licensed under the Apache License, Version 2.0 (the "License");
026: * you may not use this file except in compliance with the License.
027: * You may obtain a copy of the License at
028: *
029: * http://www.apache.org/licenses/LICENSE-2.0
030: *
031: * Unless required by applicable law or agreed to in writing, software
032: * distributed under the License is distributed on an "AS IS" BASIS,
033: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
034: * See the License for the specific language governing permissions and
035: * limitations under the License.
036: */
037:
038: package com.nabhinc.ws.service.adapter;
039:
040: import java.lang.reflect.InvocationTargetException;
041: import java.lang.reflect.Method;
042: import java.util.Properties;
043:
044: import javax.naming.Context;
045: import javax.naming.InitialContext;
046: import javax.naming.NamingException;
047:
048: import com.nabhinc.ws.core.WebServiceException;
049: import com.nabhinc.ws.server.RequestInfo;
050: import com.nabhinc.ws.service.core.JavaDelegateWebService;
051:
052: /**
053: * A Web service implementation that delegates operation
054: * execution to the corresponding method on a CORBA proxy.
055: * <p/>
056: * Service Properties (* indicates required properties):<br/>
057: * <ul>
058: * <li>scope - "service", "session", "request". The
059: * default is "service".</li>
060: * <li>attributeName - Specifies name of the session/appliaction attribute
061: * used for caching the implementation object. Applicable only when
062: * the scope is "session" or "application". </li>
063: * <li>homeJndiName* - JNDI name of EJB home.</li>
064: * <li>remoteHomeInterfaceName - Home interface class if the EJB is remote.</li>
065: * <li>localHomeInterfaceName - Home interface class if the EJB is local. Either
066: * this property or remoteHomeInterfaceName property must be specified. </li>
067: * <li>beanInterfaceClass* - EJB interface class </li>
068: * <li>jndiContextClass - JNDI context class </li>
069: * <li>jndiURL - JNDI URL</li>
070: * <li>jndiUsername - JNDI user name</li>
071: * <li>jndiPassword - JNDI password</li>
072: * </ul>
073: * <p/>
074: * In addition you can specify properties to be set on the delegate
075: * Java bean object. These properties will depend on the particular
076: * Java bean class you are using and cannot be enumerated here.
077: *
078: * @author Padmanabh Dabke
079: * (c) 2005 Nabh Information Systems, Inc. All Rights Reserved.
080: */
081: public class EJBAdapter extends JavaDelegateWebService {
082:
083: protected static final Class[] empty_class_array = new Class[0];
084: protected static final Object[] empty_object_array = new Object[0];
085:
086: private static InitialContext cached_context = null;
087: private String homeJndiName = null;
088: private String remoteHomeInterfaceName = null;
089: private String beanInterfaceClass = null;
090: private String localHomeInterfaceName = null;
091:
092: private String jndiContextClass = null;
093: private String jndiURL = null;
094: private String jndiUsername = null;
095: private String jndiPassword = null;
096:
097: protected void checkProperties() throws WebServiceException {
098: super .checkProperties();
099: if (homeJndiName == null)
100: throw new WebServiceException("Missing EJB home JNDI name.");
101: if (beanInterfaceClass == null)
102: throw new WebServiceException(
103: "Missing EJB interface class name.");
104: if (remoteHomeInterfaceName == null
105: && localHomeInterfaceName == null) {
106: throw new WebServiceException(
107: "Either remoteHomeInterfaceName or localHomeInterfaceName property must be specified.");
108: }
109: }
110:
111: ///////////////////////////////////////////////////////////////
112: ///////////////////////////////////////////////////////////////
113: /////// Default methods from JavaProvider ancestor, overridden
114: /////// for ejbeans
115: ///////////////////////////////////////////////////////////////
116: ///////////////////////////////////////////////////////////////
117:
118: /**
119: * Return a object which implements the service.
120: *
121: * @param msgContext the message context
122: * @param clsName The JNDI name of the EJB home class
123: * @return an object that implements the service
124: */
125: protected Object createDelegateInstance()
126: throws WebServiceException {
127: try {
128: if (remoteHomeInterfaceName == null) {
129: return createLocalEJB(Class
130: .forName(localHomeInterfaceName));
131: } else {
132: return createRemoteEJB(Class
133: .forName(remoteHomeInterfaceName));
134: }
135: } catch (ClassNotFoundException e) {
136: throw new WebServiceException(
137: "Home interface class not found.", e);
138: }
139: }
140:
141: /**
142: * Create an EJB using a remote home object
143: *
144: * @param msgContext the message context
145: * @param beanJndiName The JNDI name of the EJB remote home class
146: * @param homeClass the class of the home interface
147: * @return an EJB
148: */
149: private Object createRemoteEJB(Class homeClass)
150: throws WebServiceException {
151: // Get the EJB Home object from JNDI
152: Object ejbHome = getEJBHome(homeJndiName);
153: Object ehome = javax.rmi.PortableRemoteObject.narrow(ejbHome,
154: homeClass);
155:
156: // Invoke the create method of the ejbHome class without actually
157: // touching any EJB classes (i.e. no cast to EJBHome)
158: try {
159: Method createMethod = homeClass.getMethod("create",
160: empty_class_array);
161: Object result = createMethod.invoke(ehome,
162: empty_object_array);
163:
164: return result;
165: } catch (NoSuchMethodException e) {
166: throw new WebServiceException(
167: "Could not find create method on home class.", e);
168: } catch (Exception e) {
169: throw new WebServiceException(
170: "Error in invoking create method on EJB home class.",
171: e);
172: }
173: }
174:
175: /**
176: * Create an EJB using a local home object
177: *
178: * @param msgContext the message context
179: * @param beanJndiName The JNDI name of the EJB local home class
180: * @param homeClass the class of the home interface
181: * @return an EJB
182: */
183: private Object createLocalEJB(Class homeClass)
184: throws WebServiceException {
185: // Get the EJB Home object from JNDI
186: Object ejbHome = getEJBHome(homeJndiName);
187:
188: // the home object is a local home object
189: Object ehome;
190: if (homeClass.isInstance(ejbHome))
191: ehome = ejbHome;
192: else
193: throw new WebServiceException("Invalid EJB home class: "
194: + homeClass.getName() + ".");
195:
196: // Invoke the create method of the ejbHome class without actually
197: // touching any EJB classes (i.e. no cast to EJBLocalHome)
198: try {
199: Method createMethod = homeClass.getMethod("create",
200: empty_class_array);
201: Object result = createMethod.invoke(ehome,
202: empty_object_array);
203:
204: return result;
205: } catch (NoSuchMethodException e) {
206: throw new WebServiceException(
207: "Could not find create method on home class.", e);
208: } catch (Exception e) {
209: throw new WebServiceException(
210: "Error in invoking create method on EJB home class.",
211: e);
212: }
213: }
214:
215: /**
216: * Tells if the ejb that will be used to handle this service is a remote
217: * one
218: */
219: private boolean isRemoteEjb() {
220: return remoteHomeInterfaceName != null;
221: }
222:
223: /**
224: * Tells if the ejb that will be used to handle this service is a local
225: * one
226: */
227: private boolean isLocalEjb() {
228: return (!isRemoteEjb()) && (localHomeInterfaceName != null);
229: }
230:
231: /**
232: * Get the remote interface of an ejb from its home class.
233: * This function can only be used for remote ejbs
234: *
235: * @param beanJndiName the jndi name of the ejb
236: * @param service the soap service
237: * @param msgContext the message context (can be null)
238: */
239: private Class getRemoteInterfaceClassFromHome(String beanJndiName)
240: throws WebServiceException {
241:
242: try {
243: // Get the EJB Home object from JNDI
244: Object ejbHome = getEJBHome(beanJndiName);
245:
246: //ClassLoader cl = Thread.currentThread().getContextClassLoader();
247: Class homeClass = Class.forName(remoteHomeInterfaceName);
248: // ClassUtils.forName(remoteHomeInterfaceName, true, cl);
249:
250: // Make sure the object we got back from JNDI is the same type
251: // as the what is specified in the config file
252: Object ehome = javax.rmi.PortableRemoteObject.narrow(
253: ejbHome, homeClass);
254:
255: // This code requires the use of ejb.jar, so we do the stuff below
256: // EJBHome ejbHome = (EJBHome) ehome;
257: // EJBMetaData meta = ejbHome.getEJBMetaData();
258: // Class interfaceClass = meta.getRemoteInterfaceClass();
259:
260: // Invoke the getEJBMetaData method of the ejbHome class without
261: // actually touching any EJB classes (i.e. no cast to EJBHome)
262: Method getEJBMetaData = homeClass.getMethod(
263: "getEJBMetaData", empty_class_array);
264: Object metaData = getEJBMetaData.invoke(ehome,
265: empty_object_array);
266: Method getRemoteInterfaceClass = metaData.getClass()
267: .getMethod("getRemoteInterfaceClass",
268: empty_class_array);
269: return (Class) getRemoteInterfaceClass.invoke(metaData,
270: empty_object_array);
271: } catch (ClassNotFoundException e) {
272: throw new WebServiceException(
273: "Could not find home interface class.", e);
274: } catch (Exception e) {
275: throw new WebServiceException(
276: "Failed to get remote interface class name.", e);
277: }
278: }
279:
280: /**
281: * Get the class description for the EJB Remote or Local Interface,
282: * which is what we are interested in exposing to the world (i.e. in WSDL).
283: *
284: * @param msgContext the message context (can be null)
285: * @param beanJndiName the JNDI name of the EJB
286: * @return the class info of the EJB remote or local interface
287: */
288: protected Class getServiceClass(String beanJndiName)
289: throws WebServiceException {
290: Class interfaceClass = null;
291:
292: if (beanInterfaceClass != null) {
293: //ClassLoader cl = Thread.currentThread().getContextClassLoader();
294: try {
295: interfaceClass = Class.forName(beanInterfaceClass);
296: /* ClassUtils.forName(beanInterfaceName,
297: true,
298: cl);
299: */
300: } catch (ClassNotFoundException e) {
301: throw new WebServiceException(
302: "Failed to find interface class.", e);
303: }
304: } else {
305: // cannot get the interface name from the configuration, we get
306: // it from the EJB Home (if remote)
307: if (isRemoteEjb()) {
308: interfaceClass = getRemoteInterfaceClassFromHome(beanJndiName);
309: } else if (isLocalEjb()) {
310: // we cannot get the local interface from the local ejb home
311: // localInterfaceName is mandatory for local ejbs
312: throw new WebServiceException(
313: "localInterfaceName must be specified for local EJBs");
314: } else {
315: // neither a local ejb or a remote one ...
316: throw new WebServiceException(
317: "Could not determine service class.");
318: }
319: }
320:
321: // got it, return it
322: return interfaceClass;
323: }
324:
325: /**
326: * Common routine to do the JNDI lookup on the Home interface object
327: * username and password for jndi lookup are got from the configuration or from
328: * the messageContext if not found in the configuration
329: */
330: private Object getEJBHome(String beanJndiName)
331: throws WebServiceException {
332: Object ejbHome = null;
333:
334: // Set up an InitialContext and use it get the beanJndiName from JNDI
335: try {
336: Properties properties = new Properties();
337:
338: // collect all the properties we need to access JNDI:
339: // username, password, factoryclass, contextUrl
340:
341: // username
342: if (jndiUsername != null) {
343: properties.setProperty(Context.SECURITY_PRINCIPAL,
344: jndiUsername);
345: }
346:
347: // password
348: if (jndiPassword != null) {
349: properties.setProperty(Context.SECURITY_CREDENTIALS,
350: jndiPassword);
351: }
352:
353: // factory class
354: if (jndiContextClass != null) {
355: properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
356: jndiContextClass);
357: }
358:
359: // contextUrl
360: if (jndiURL != null) {
361: properties.setProperty(Context.PROVIDER_URL, jndiURL);
362: }
363:
364: if (properties.size() == 0)
365: properties = null;
366:
367: // get context using these properties
368: InitialContext context = getContext(properties);
369:
370: // if we didn't get a context, fail
371: if (context == null)
372: throw new WebServiceException(
373: "Failed to create initial context.");
374:
375: ejbHome = getEJBHome(context, beanJndiName);
376:
377: if (ejbHome == null)
378: throw new WebServiceException(
379: "Failed to create EJB home.");
380: } catch (NamingException e) {
381: throw new WebServiceException("JNDI exception.", e);
382: }
383:
384: return ejbHome;
385: }
386:
387: protected InitialContext getCachedContext()
388: throws javax.naming.NamingException {
389: if (cached_context == null)
390: cached_context = new InitialContext();
391: return cached_context;
392: }
393:
394: protected InitialContext getContext(Properties properties)
395: throws javax.naming.NamingException {
396: // if we got any stuff from the configuration file
397: // create a new context using these properties
398: // otherwise, we get a default context and cache it for next time
399: return ((properties == null) ? getCachedContext()
400: : new InitialContext(properties));
401: }
402:
403: protected Object getEJBHome(InitialContext context,
404: String beanJndiName) throws javax.naming.NamingException {
405: // Do the JNDI lookup
406: return context.lookup(beanJndiName);
407: }
408:
409: /**
410: * Override the default implementation such that we can include
411: * special handling for {@link java.rmi.ServerException}.
412: * <p/>
413: * Converts {@link java.rmi.ServerException} exceptions to
414: * {@link InvocationTargetException} exceptions with the same cause.
415: * This allows the axis framework to create a SOAP fault.
416: * </p>
417: *
418: * @see org.apache.axis.providers.java.RPCProvider#invokeMethod(org.apache.axis.MessageContext, java.lang.reflect.Method, java.lang.Object, java.lang.Object[])
419: */
420: public Object invoke(RequestInfo reqInfo)
421: throws WebServiceException {
422: try {
423: return super .invoke(reqInfo);
424: } catch (WebServiceException e) {
425: Throwable cause = e.getCause();
426: if (cause instanceof java.rmi.ServerException) {
427: java.rmi.ServerException se = (java.rmi.ServerException) cause;
428: throw new WebServiceException(se.getMessage(), se
429: .getCause());
430: }
431: throw e;
432: }
433: }
434:
435: public void setBeanInterfaceClass(String beanInterfaceClass) {
436: this .beanInterfaceClass = beanInterfaceClass;
437: }
438:
439: public void setHomeJndiName(String homeJndiName) {
440: this .homeJndiName = homeJndiName;
441: }
442:
443: public void setJndiContextClass(String jndiContextClass) {
444: this .jndiContextClass = jndiContextClass;
445: }
446:
447: public void setJndiPassword(String jndiPassword) {
448: this .jndiPassword = jndiPassword;
449: }
450:
451: public void setJndiURL(String jndiURL) {
452: this .jndiURL = jndiURL;
453: }
454:
455: public void setJndiUsername(String jndiUsername) {
456: this .jndiUsername = jndiUsername;
457: }
458:
459: public void setLocalHomeInterfaceName(String localHomeInterfaceName) {
460: this .localHomeInterfaceName = localHomeInterfaceName;
461: }
462:
463: public void setRemoteHomeInterfaceName(
464: String remoteHomeInterfaceName) {
465: this.remoteHomeInterfaceName = remoteHomeInterfaceName;
466: }
467: }
|