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