001: /*
002: * Copyright 2002-2007 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.support;
018:
019: import javax.ejb.EnterpriseBean;
020:
021: import org.springframework.beans.BeansException;
022: import org.springframework.beans.FatalBeanException;
023: import org.springframework.beans.factory.BeanFactory;
024: import org.springframework.beans.factory.access.BeanFactoryLocator;
025: import org.springframework.beans.factory.access.BeanFactoryReference;
026: import org.springframework.context.access.ContextJndiBeanFactoryLocator;
027: import org.springframework.util.WeakReferenceMonitor;
028:
029: /**
030: * Base class for all Spring-based EJBs. Not intended for direct subclassing:
031: * Extend {@link AbstractStatelessSessionBean}, {@link AbstractStatefulSessionBean}
032: * or {@link AbstractMessageDrivenBean} instead.
033: *
034: * <p>Provides a standard way of loading a Spring BeanFactory. Subclasses act as a
035: * facade, with the business logic deferred to beans in the BeanFactory. Default
036: * is to use a {@link org.springframework.context.access.ContextJndiBeanFactoryLocator},
037: * which will initialize an XML ApplicationContext from the class path (based on a JNDI
038: * name specified). For a different locator strategy, <code>setBeanFactoryLocator</code>
039: * may be called (<i>before</i> your EJB's <code>ejbCreate</code> method is invoked,
040: * e.g. in <code>setSessionContext</code>). For use of a shared ApplicationContext between
041: * multiple EJBs, where the container class loader setup supports this visibility, you may
042: * instead use a {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator}.
043: * Alternatively, {@link #setBeanFactoryLocator} may be called with a custom implementation
044: * of the {@link org.springframework.beans.factory.access.BeanFactoryLocator} interface.
045: *
046: * <p>Note that we cannot use <code>final</code> for our implementation of EJB lifecycle
047: * methods, as this would violate the EJB specification.
048: *
049: * @author Rod Johnson
050: * @author Colin Sampaleanu
051: * @author Juergen Hoeller
052: * @see org.springframework.context.access.ContextJndiBeanFactoryLocator
053: * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
054: */
055: public abstract class AbstractEnterpriseBean implements EnterpriseBean {
056:
057: public static final String BEAN_FACTORY_PATH_ENVIRONMENT_KEY = "java:comp/env/ejb/BeanFactoryPath";
058:
059: /**
060: * Helper strategy that knows how to locate a Spring BeanFactory (or
061: * ApplicationContext).
062: */
063: private BeanFactoryLocator beanFactoryLocator;
064:
065: /** factoryKey to be used with BeanFactoryLocator */
066: private String beanFactoryLocatorKey;
067:
068: /** Spring BeanFactory that provides the namespace for this EJB */
069: private BeanFactoryReference beanFactoryReference;
070:
071: /**
072: * Set the BeanFactoryLocator to use for this EJB. Default is a
073: * ContextJndiBeanFactoryLocator.
074: * <p>Can be invoked before loadBeanFactory, for example in constructor or
075: * setSessionContext if you want to override the default locator.
076: * <p>Note that the BeanFactory is automatically loaded by the ejbCreate
077: * implementations of AbstractStatelessSessionBean and
078: * AbstractMessageDriverBean but needs to be explicitly loaded in custom
079: * AbstractStatefulSessionBean ejbCreate methods.
080: * @see AbstractStatelessSessionBean#ejbCreate
081: * @see AbstractMessageDrivenBean#ejbCreate
082: * @see AbstractStatefulSessionBean#loadBeanFactory
083: * @see org.springframework.context.access.ContextJndiBeanFactoryLocator
084: */
085: public void setBeanFactoryLocator(
086: BeanFactoryLocator beanFactoryLocator) {
087: this .beanFactoryLocator = beanFactoryLocator;
088: }
089:
090: /**
091: * Set the bean factory locator key.
092: * <p>In case of the default BeanFactoryLocator implementation,
093: * ContextJndiBeanFactoryLocator, this is the JNDI path. The default value
094: * of this property is "java:comp/env/ejb/BeanFactoryPath".
095: * <p>Can be invoked before loadBeanFactory, for example in constructor or
096: * setSessionContext if you want to override the default locator key.
097: * @see #BEAN_FACTORY_PATH_ENVIRONMENT_KEY
098: */
099: public void setBeanFactoryLocatorKey(String factoryKey) {
100: this .beanFactoryLocatorKey = factoryKey;
101: }
102:
103: /**
104: * Load a Spring BeanFactory namespace. Subclasses must invoke this method.
105: * <p>Package-visible as it shouldn't be called directly by user-created
106: * subclasses.
107: * @see org.springframework.ejb.support.AbstractStatelessSessionBean#ejbCreate()
108: */
109: void loadBeanFactory() throws BeansException {
110: if (this .beanFactoryLocator == null) {
111: this .beanFactoryLocator = new ContextJndiBeanFactoryLocator();
112: }
113: if (this .beanFactoryLocatorKey == null) {
114: this .beanFactoryLocatorKey = BEAN_FACTORY_PATH_ENVIRONMENT_KEY;
115: }
116:
117: this .beanFactoryReference = this .beanFactoryLocator
118: .useBeanFactory(this .beanFactoryLocatorKey);
119:
120: // We cannot rely on the container to call ejbRemove() (it's skipped in
121: // the case of system exceptions), so ensure the the bean factory
122: // reference is eventually released.
123: WeakReferenceMonitor.monitor(this ,
124: new BeanFactoryReferenceReleaseListener(
125: this .beanFactoryReference));
126: }
127:
128: /**
129: * Unload the Spring BeanFactory instance. The default {@link #ejbRemove()}
130: * method invokes this method, but subclasses which override <code>ejbRemove</code>
131: * must invoke this method themselves.
132: * <p>Package-visible as it shouldn't be called directly by user-created
133: * subclasses.
134: */
135: void unloadBeanFactory() throws FatalBeanException {
136: // We will not ever get here if the container skips calling ejbRemove(),
137: // but the WeakReferenceMonitor will still clean up (later) in that case.
138: if (this .beanFactoryReference != null) {
139: this .beanFactoryReference.release();
140: this .beanFactoryReference = null;
141: }
142: }
143:
144: /**
145: * May be called after ejbCreate().
146: * @return the bean factory
147: */
148: protected BeanFactory getBeanFactory() {
149: return this .beanFactoryReference.getFactory();
150: }
151:
152: /**
153: * EJB lifecycle method, implemented to invoke onEjbRemote and unload the
154: * BeanFactory afterwards.
155: * <p>Don't override it (although it can't be made final): code your shutdown
156: * in onEjbRemove.
157: * @see #onEjbRemove
158: */
159: public void ejbRemove() {
160: onEjbRemove();
161: unloadBeanFactory();
162: }
163:
164: /**
165: * Subclasses must implement this method to do any initialization they would
166: * otherwise have done in an ejbRemove() method. The BeanFactory will be
167: * unloaded afterwards.
168: * <p>This implementation is empty, to be overridden in subclasses. The same
169: * restrictions apply to the work of this method as to an ejbRemove() method.
170: */
171: protected void onEjbRemove() {
172: // empty
173: }
174:
175: /**
176: * Implementation of WeakReferenceMonitor's ReleaseListener callback interface.
177: * Release the given BeanFactoryReference if the monitor detects that there
178: * are no strong references to the handle anymore.
179: */
180: private static class BeanFactoryReferenceReleaseListener implements
181: WeakReferenceMonitor.ReleaseListener {
182:
183: private final BeanFactoryReference beanFactoryReference;
184:
185: public BeanFactoryReferenceReleaseListener(
186: BeanFactoryReference beanFactoryReference) {
187: this .beanFactoryReference = beanFactoryReference;
188: }
189:
190: public void released() {
191: this.beanFactoryReference.release();
192: }
193: }
194:
195: }
|