001: /*
002: * Copyright 2002-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.ejb.access;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021:
022: import javax.naming.NamingException;
023:
024: import org.aopalliance.intercept.MethodInterceptor;
025:
026: import org.springframework.jndi.JndiObjectLocator;
027:
028: /**
029: * Superclass for AOP interceptors invoking local or remote Stateless Session Beans.
030: *
031: * <p>Such an interceptor must be the last interceptor in the advice chain.
032: * In this case, there is no direct target object: The call is handled in a
033: * special way, getting executed on an EJB instance retrieved via an EJB home.
034: *
035: * @author Rod Johnson
036: * @author Juergen Hoeller
037: */
038: public abstract class AbstractSlsbInvokerInterceptor extends
039: JndiObjectLocator implements MethodInterceptor {
040:
041: private boolean lookupHomeOnStartup = true;
042:
043: private boolean cacheHome = true;
044:
045: /**
046: * The EJB's home object, potentially cached.
047: * The type must be Object as it could be either EJBHome or EJBLocalHome.
048: */
049: private Object cachedHome;
050:
051: /**
052: * The no-arg create() method required on EJB homes, potentially cached.
053: */
054: private Method createMethod;
055:
056: private final Object homeMonitor = new Object();
057:
058: /**
059: * Set whether to look up the EJB home object on startup.
060: * Default is "true".
061: * <p>Can be turned off to allow for late start of the EJB server.
062: * In this case, the EJB home object will be fetched on first access.
063: * @see #setCacheHome
064: */
065: public void setLookupHomeOnStartup(boolean lookupHomeOnStartup) {
066: this .lookupHomeOnStartup = lookupHomeOnStartup;
067: }
068:
069: /**
070: * Set whether to cache the EJB home object once it has been located.
071: * Default is "true".
072: * <p>Can be turned off to allow for hot restart of the EJB server.
073: * In this case, the EJB home object will be fetched for each invocation.
074: * @see #setLookupHomeOnStartup
075: */
076: public void setCacheHome(boolean cacheHome) {
077: this .cacheHome = cacheHome;
078: }
079:
080: /**
081: * Fetches EJB home on startup, if necessary.
082: * @see #setLookupHomeOnStartup
083: * @see #refreshHome
084: */
085: public void afterPropertiesSet() throws NamingException {
086: super .afterPropertiesSet();
087: if (this .lookupHomeOnStartup) {
088: // look up EJB home and create method
089: refreshHome();
090: }
091: }
092:
093: /**
094: * Refresh the cached home object, if applicable.
095: * Also caches the create method on the home object.
096: * @throws NamingException if thrown by the JNDI lookup
097: * @see #lookup
098: * @see #getCreateMethod
099: */
100: protected void refreshHome() throws NamingException {
101: synchronized (this .homeMonitor) {
102: Object home = lookup();
103: if (this .cacheHome) {
104: this .cachedHome = home;
105: this .createMethod = getCreateMethod(home);
106: }
107: }
108: }
109:
110: /**
111: * Determine the create method of the given EJB home object.
112: * @param home the EJB home object
113: * @return the create method
114: * @throws EjbAccessException if the method couldn't be retrieved
115: */
116: protected Method getCreateMethod(Object home)
117: throws EjbAccessException {
118: try {
119: // Cache the EJB create() method that must be declared on the home interface.
120: return home.getClass().getMethod("create", (Class[]) null);
121: } catch (NoSuchMethodException ex) {
122: throw new EjbAccessException("EJB home [" + home
123: + "] has no no-arg create() method");
124: }
125: }
126:
127: /**
128: * Return the EJB home object to use. Called for each invocation.
129: * <p>Default implementation returns the home created on initialization,
130: * if any; else, it invokes lookup to get a new proxy for each invocation.
131: * <p>Can be overridden in subclasses, for example to cache a home object
132: * for a given amount of time before recreating it, or to test the home
133: * object whether it is still alive.
134: * @return the EJB home object to use for an invocation
135: * @throws NamingException if proxy creation failed
136: * @see #lookup
137: * @see #getCreateMethod
138: */
139: protected Object getHome() throws NamingException {
140: if (!this .cacheHome
141: || (this .lookupHomeOnStartup && !isHomeRefreshable())) {
142: return (this .cachedHome != null ? this .cachedHome
143: : lookup());
144: } else {
145: synchronized (this .homeMonitor) {
146: if (this .cachedHome == null) {
147: this .cachedHome = lookup();
148: this .createMethod = getCreateMethod(this .cachedHome);
149: }
150: return this .cachedHome;
151: }
152: }
153: }
154:
155: /**
156: * Return whether the cached EJB home object is potentially
157: * subject to on-demand refreshing. Default is "false".
158: */
159: protected boolean isHomeRefreshable() {
160: return false;
161: }
162:
163: /**
164: * Invoke the create() method on the cached EJB home object.
165: * @return a new EJBObject or EJBLocalObject
166: * @throws NamingException if thrown by JNDI
167: * @throws InvocationTargetException if thrown by the create method
168: */
169: protected Object create() throws NamingException,
170: InvocationTargetException {
171: try {
172: Object home = getHome();
173: Method createMethodToUse = this .createMethod;
174: if (createMethodToUse == null) {
175: createMethodToUse = getCreateMethod(home);
176: }
177: // Invoke create() method on EJB home object.
178: return createMethodToUse.invoke(home, (Object[]) null);
179: } catch (IllegalAccessException ex) {
180: throw new EjbAccessException(
181: "Could not access EJB home create() method", ex);
182: }
183: }
184:
185: }
|