/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.ejb.access;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.naming.Context;
import javax.naming.NamingException;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import org.springframework.jndi.JndiObjectLocator;
import org.springframework.lang.Nullable;

Base class for AOP interceptors invoking local or remote Stateless Session Beans. Designed for EJB 2.x, but works for EJB 3 Session Beans as well.

Such an interceptor must be the last interceptor in the advice chain. In this case, there is no direct target object: The call is handled in a special way, getting executed on an EJB instance retrieved via an EJB home.

Author:Rod Johnson, Juergen Hoeller
/** * Base class for AOP interceptors invoking local or remote Stateless Session Beans. * Designed for EJB 2.x, but works for EJB 3 Session Beans as well. * * <p>Such an interceptor must be the last interceptor in the advice chain. * In this case, there is no direct target object: The call is handled in a * special way, getting executed on an EJB instance retrieved via an EJB home. * * @author Rod Johnson * @author Juergen Hoeller */
public abstract class AbstractSlsbInvokerInterceptor extends JndiObjectLocator implements MethodInterceptor { private boolean lookupHomeOnStartup = true; private boolean cacheHome = true; private boolean exposeAccessContext = false;
The EJB's home object, potentially cached. The type must be Object as it could be either EJBHome or EJBLocalHome.
/** * The EJB's home object, potentially cached. * The type must be Object as it could be either EJBHome or EJBLocalHome. */
@Nullable private Object cachedHome;
The no-arg create() method required on EJB homes, potentially cached.
/** * The no-arg create() method required on EJB homes, potentially cached. */
@Nullable private Method createMethod; private final Object homeMonitor = new Object();
Set whether to look up the EJB home object on startup. Default is "true".

Can be turned off to allow for late start of the EJB server. In this case, the EJB home object will be fetched on first access.

See Also:
  • setCacheHome
/** * Set whether to look up the EJB home object on startup. * Default is "true". * <p>Can be turned off to allow for late start of the EJB server. * In this case, the EJB home object will be fetched on first access. * @see #setCacheHome */
public void setLookupHomeOnStartup(boolean lookupHomeOnStartup) { this.lookupHomeOnStartup = lookupHomeOnStartup; }
Set whether to cache the EJB home object once it has been located. Default is "true".

Can be turned off to allow for hot restart of the EJB server. In this case, the EJB home object will be fetched for each invocation.

See Also:
  • setLookupHomeOnStartup
/** * Set whether to cache the EJB home object once it has been located. * Default is "true". * <p>Can be turned off to allow for hot restart of the EJB server. * In this case, the EJB home object will be fetched for each invocation. * @see #setLookupHomeOnStartup */
public void setCacheHome(boolean cacheHome) { this.cacheHome = cacheHome; }
Set whether to expose the JNDI environment context for all access to the target EJB, i.e. for all method invocations on the exposed object reference.

Default is "false", i.e. to only expose the JNDI context for object lookup. Switch this flag to "true" in order to expose the JNDI environment (including the authorization context) for each EJB invocation, as needed by WebLogic for EJBs with authorization requirements.

/** * Set whether to expose the JNDI environment context for all access to the target * EJB, i.e. for all method invocations on the exposed object reference. * <p>Default is "false", i.e. to only expose the JNDI context for object lookup. * Switch this flag to "true" in order to expose the JNDI environment (including * the authorization context) for each EJB invocation, as needed by WebLogic * for EJBs with authorization requirements. */
public void setExposeAccessContext(boolean exposeAccessContext) { this.exposeAccessContext = exposeAccessContext; }
Fetches EJB home on startup, if necessary.
See Also:
/** * Fetches EJB home on startup, if necessary. * @see #setLookupHomeOnStartup * @see #refreshHome */
@Override public void afterPropertiesSet() throws NamingException { super.afterPropertiesSet(); if (this.lookupHomeOnStartup) { // look up EJB home and create method refreshHome(); } }
Refresh the cached home object, if applicable. Also caches the create method on the home object.
Throws:
  • NamingException – if thrown by the JNDI lookup
See Also:
/** * Refresh the cached home object, if applicable. * Also caches the create method on the home object. * @throws NamingException if thrown by the JNDI lookup * @see #lookup * @see #getCreateMethod */
protected void refreshHome() throws NamingException { synchronized (this.homeMonitor) { Object home = lookup(); if (this.cacheHome) { this.cachedHome = home; this.createMethod = getCreateMethod(home); } } }
Determine the create method of the given EJB home object.
Params:
  • home – the EJB home object
Throws:
Returns:the create method
/** * Determine the create method of the given EJB home object. * @param home the EJB home object * @return the create method * @throws EjbAccessException if the method couldn't be retrieved */
@Nullable protected Method getCreateMethod(Object home) throws EjbAccessException { try { // Cache the EJB create() method that must be declared on the home interface. return home.getClass().getMethod("create"); } catch (NoSuchMethodException ex) { throw new EjbAccessException("EJB home [" + home + "] has no no-arg create() method"); } }
Return the EJB home object to use. Called for each invocation.

Default implementation returns the home created on initialization, if any; else, it invokes lookup to get a new proxy for each invocation.

Can be overridden in subclasses, for example to cache a home object for a given amount of time before recreating it, or to test the home object whether it is still alive.

Throws:
See Also:
Returns:the EJB home object to use for an invocation
/** * Return the EJB home object to use. Called for each invocation. * <p>Default implementation returns the home created on initialization, * if any; else, it invokes lookup to get a new proxy for each invocation. * <p>Can be overridden in subclasses, for example to cache a home object * for a given amount of time before recreating it, or to test the home * object whether it is still alive. * @return the EJB home object to use for an invocation * @throws NamingException if proxy creation failed * @see #lookup * @see #getCreateMethod */
protected Object getHome() throws NamingException { if (!this.cacheHome || (this.lookupHomeOnStartup && !isHomeRefreshable())) { return (this.cachedHome != null ? this.cachedHome : lookup()); } else { synchronized (this.homeMonitor) { if (this.cachedHome == null) { this.cachedHome = lookup(); this.createMethod = getCreateMethod(this.cachedHome); } return this.cachedHome; } } }
Return whether the cached EJB home object is potentially subject to on-demand refreshing. Default is "false".
/** * Return whether the cached EJB home object is potentially * subject to on-demand refreshing. Default is "false". */
protected boolean isHomeRefreshable() { return false; }
Prepares the thread context if necessary, and delegates to invokeInContext.
/** * Prepares the thread context if necessary, and delegates to * {@link #invokeInContext}. */
@Override @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { Context ctx = (this.exposeAccessContext ? getJndiTemplate().getContext() : null); try { return invokeInContext(invocation); } finally { getJndiTemplate().releaseContext(ctx); } }
Perform the given invocation on the current EJB home, within the thread context being prepared accordingly. Template method to be implemented by subclasses.
Params:
  • invocation – the AOP method invocation
Throws:
Returns:the invocation result, if any
/** * Perform the given invocation on the current EJB home, * within the thread context being prepared accordingly. * Template method to be implemented by subclasses. * @param invocation the AOP method invocation * @return the invocation result, if any * @throws Throwable in case of invocation failure */
@Nullable protected abstract Object invokeInContext(MethodInvocation invocation) throws Throwable;
Invokes the create() method on the cached EJB home object.
Throws:
Returns:a new EJBObject or EJBLocalObject
/** * Invokes the {@code create()} method on the cached EJB home object. * @return a new EJBObject or EJBLocalObject * @throws NamingException if thrown by JNDI * @throws InvocationTargetException if thrown by the create method */
protected Object create() throws NamingException, InvocationTargetException { try { Object home = getHome(); Method createMethodToUse = this.createMethod; if (createMethodToUse == null) { createMethodToUse = getCreateMethod(home); } if (createMethodToUse == null) { return home; } // Invoke create() method on EJB home object. return createMethodToUse.invoke(home, (Object[]) null); } catch (IllegalAccessException ex) { throw new EjbAccessException("Could not access EJB home create() method", ex); } } }