001: /**
002: * EasyBeans
003: * Copyright (C) 2006-2007 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: StatefulSessionFactory.java 1970 2007-10-16 11:49:25Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.container.session.stateful;
025:
026: import java.lang.reflect.InvocationTargetException;
027: import java.lang.reflect.Method;
028:
029: import javax.ejb.ApplicationException;
030: import javax.ejb.EJBException;
031: import javax.ejb.NoSuchEJBException;
032: import javax.ejb.Timer;
033:
034: import org.ow2.easybeans.api.EZBContainer;
035: import org.ow2.easybeans.api.FactoryException;
036: import org.ow2.easybeans.api.bean.EasyBeansSFSB;
037: import org.ow2.easybeans.api.pool.PoolException;
038: import org.ow2.easybeans.container.session.SessionFactory;
039: import org.ow2.easybeans.pool.JPool;
040: import org.ow2.easybeans.pool.PoolEntryStatistics;
041: import org.ow2.easybeans.pool.PoolFactory;
042: import org.ow2.easybeans.rpc.JEJBResponse;
043: import org.ow2.easybeans.rpc.api.EJBResponse;
044: import org.ow2.easybeans.rpc.api.RPCException;
045: import org.ow2.util.log.Log;
046: import org.ow2.util.log.LogFactory;
047:
048: /**
049: * This class manages the stateless session bean and its creation/lifecycle.
050: * @author Florent Benoit
051: */
052: public class StatefulSessionFactory extends
053: SessionFactory<EasyBeansSFSB> implements
054: PoolFactory<EasyBeansSFSB, Long> {
055:
056: /**
057: * Logger.
058: */
059: private static Log logger = LogFactory
060: .getLog(StatefulSessionFactory.class);
061:
062: /**
063: * Id generator.
064: */
065: private long idCount = 0L;
066:
067: /**
068: * Builds a new factory with a given name and its container.
069: * @param className name of this factory (name of class that is managed)
070: * @param container the root component of this factory.
071: * @throws FactoryException if class can't be loaded.
072: */
073: public StatefulSessionFactory(final String className,
074: final EZBContainer container) throws FactoryException {
075: super (className, container);
076: JPool<EasyBeansSFSB, Long> pool = new JPool<EasyBeansSFSB, Long>(
077: this );
078: // stateful = only one client at a given time.
079: pool.setAllowSharedInstance(false);
080: setPool(pool);
081: }
082:
083: /**
084: * Checks if the given object with the given clue is matching.
085: * @param bean given object against which the check should be done.
086: * @param clue the object used as clue to check the matching.
087: * @return true if it is matching, else false.
088: */
089: public boolean isMatching(final EasyBeansSFSB bean, final Long clue) {
090: logger.debug("Called with bean = {0} and beanId = {1}", bean,
091: clue);
092: Long beanId = bean.getEasyBeansStatefulID();
093: boolean val = (beanId.equals(clue));
094: logger.debug("Found id = {0} and will return = {1}", beanId,
095: Boolean.valueOf(val));
096: return val;
097: }
098:
099: /**
100: * Validate an instance by giving some statistics.
101: * @param object the instance to validate
102: * @param stats some statistics to help in the validating process.
103: * @return true if the element is valid, else false.
104: */
105: public boolean validate(final EasyBeansSFSB object,
106: final PoolEntryStatistics stats) {
107: return true;
108: }
109:
110: /**
111: * Gets a new ID or a null value.
112: * @param beanId given id.
113: * @return new id
114: */
115: @Override
116: protected synchronized Long getId(final Long beanId) {
117: Long newId = beanId;
118: // no Id, compute a new one
119: if (newId == null) {
120: idCount++;
121: newId = Long.valueOf(idCount);
122: }
123: return newId;
124: }
125:
126: /**
127: * Creates an instance with the given hint.
128: * @param clue a clue given by the Pool. Could be null.
129: * @throws PoolException if instance cannot be created.
130: * @return the created instance.
131: */
132: @Override
133: public synchronized EasyBeansSFSB create(final Long clue)
134: throws PoolException {
135: // else, create it
136: EasyBeansSFSB bean = super .create(clue);
137:
138: return bean;
139: }
140:
141: /**
142: * Gets a bean for the given id.
143: * @param beanId id of the expected bean.
144: * @return a Stateless bean.
145: * @throws IllegalArgumentException if bean is not found.
146: */
147: @Override
148: protected synchronized EasyBeansSFSB getBean(final Long beanId)
149: throws IllegalArgumentException {
150: EasyBeansSFSB bean = null;
151: try {
152: bean = getPool().get(beanId);
153: } catch (PoolException e) {
154: throw new IllegalArgumentException(
155: "Cannot get element in the pool", e);
156: }
157: logger.debug("Set for bean {0} the Id = {1}", bean, beanId);
158: bean.setEasyBeansStatefulID(beanId);
159: return bean;
160: }
161:
162: /**
163: * Callback called when object is gonna be removed.
164: * @param instance that is being removed from the pool.
165: */
166: @Override
167: public void remove(final EasyBeansSFSB instance) {
168: super .remove(instance);
169: instance.setEasyBeansRemoved(true);
170: }
171:
172: /**
173: * Do a local call on a method of this factory.
174: * @param hash the hash of the method to execute.
175: * @param methodArgs the arguments of the method
176: * @param beanId the id of the bean that we want (stateful).
177: * @return response container new id (if any) and value.
178: */
179: @Override
180: public EJBResponse localCall(final long hash,
181: final Object[] methodArgs, final Long beanId) {
182: Long id = getId(beanId);
183:
184: // build EJB Response and set the id
185: EJBResponse ejbResponse = new JEJBResponse();
186: ejbResponse.setBeanId(id);
187:
188: EasyBeansSFSB bean = null;
189: try {
190: bean = getBean(id);
191: } catch (IllegalArgumentException e) {
192: ejbResponse.setRPCException(new RPCException(
193: "Cannot get element in the pool", e));
194: return ejbResponse;
195: } catch (NoSuchEJBException e) {
196: ejbResponse.setRPCException(new RPCException(
197: "Bean has been removed", e));
198: return ejbResponse;
199: }
200:
201: Method m = getHashes().get(Long.valueOf(hash));
202:
203: if (m == null) {
204: ejbResponse.setRPCException(new RPCException(
205: "Cannot find method called on the bean '"
206: + getClassName() + "'."));
207: return ejbResponse;
208: }
209:
210: Object value = null;
211:
212: // set ClassLoader
213: ClassLoader oldClassLoader = Thread.currentThread()
214: .getContextClassLoader();
215: Thread.currentThread().setContextClassLoader(
216: getContainer().getClassLoader());
217: synchronized (bean) {
218: try {
219: value = m.invoke(bean, methodArgs);
220: } catch (IllegalArgumentException e) {
221: ejbResponse.setRPCException(new RPCException(e));
222: } catch (IllegalAccessException e) {
223: ejbResponse.setRPCException(new RPCException(e));
224: } catch (InvocationTargetException e) {
225: Throwable cause = e.getCause();
226: RPCException rpcException = new RPCException(cause);
227: // ApplicationException ?
228: ApplicationException applicationException = getBeanInfo()
229: .getApplicationExceptions().get(
230: cause.getClass().getName());
231: if (applicationException != null) {
232: rpcException.setApplicationException();
233: }
234: ejbResponse.setRPCException(rpcException);
235: } finally {
236: Thread.currentThread().setContextClassLoader(
237: oldClassLoader);
238: // push back into the pool
239: try {
240: getPool().release(bean);
241: } catch (PoolException e) {
242: ejbResponse.setRPCException(new RPCException(
243: "cannot release bean", e));
244: }
245:
246: // If the bean has been removed (stateful), flag it as a removed
247: // bean, so the client won't call again any methods.
248: if (bean.getEasyBeansRemoved()) {
249: ejbResponse.setRemoved(true);
250: }
251:
252: }
253: }
254: ejbResponse.setValue(value);
255: return ejbResponse;
256:
257: }
258:
259: /**
260: * Notified when the timer service send a Timer object.
261: * It has to call the Timed method.
262: * @param timer the given timer object that will be given to the timer method.
263: */
264: public void notifyTimeout(final Timer timer) {
265: throw new EJBException(
266: "Stateful bean cannot receive Timer objects");
267: }
268: }
|