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$
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.container;
025:
026: import static javax.ejb.TransactionManagementType.BEAN;
027: import static javax.ejb.TransactionManagementType.CONTAINER;
028:
029: import java.io.Serializable;
030: import java.security.Identity;
031: import java.security.Principal;
032: import java.util.Collection;
033: import java.util.Date;
034: import java.util.List;
035: import java.util.Properties;
036:
037: import javax.ejb.EJBContext;
038: import javax.ejb.EJBException;
039: import javax.ejb.EJBHome;
040: import javax.ejb.EJBLocalHome;
041: import javax.ejb.Timer;
042: import javax.ejb.TimerService;
043: import javax.ejb.TransactionManagementType;
044: import javax.naming.InitialContext;
045: import javax.naming.NamingException;
046: import javax.transaction.Status;
047: import javax.transaction.SystemException;
048: import javax.transaction.TransactionManager;
049: import javax.transaction.UserTransaction;
050:
051: import org.ow2.easybeans.api.Factory;
052: import org.ow2.easybeans.api.bean.EasyBeansBean;
053: import org.ow2.easybeans.api.container.EZBEJBContext;
054: import org.ow2.easybeans.security.propagation.context.SecurityCurrent;
055: import org.ow2.easybeans.transaction.JTransactionManager;
056: import org.ow2.util.log.Log;
057: import org.ow2.util.log.LogFactory;
058:
059: /**
060: * Class implementing the EJBContext interface.
061: * It is extended for Session context or MessageDriven Context
062: * @param <BeanType> The type of bean managed.
063: * @author Florent Benoit
064: */
065: public class EasyBeansEJBContext<BeanType extends EasyBeansBean>
066: implements EZBEJBContext<BeanType>, EJBContext {
067:
068: /**
069: * Logger.
070: */
071: private Log logger = LogFactory.getLog(EasyBeansEJBContext.class);
072:
073: /**
074: * java:comp/env prefix.
075: */
076: private static final String JAVA_COMP_ENV = "java:comp/env/";
077:
078: /**
079: * Reference to the transaction manager.
080: */
081: private TransactionManager transactionManager = null;
082:
083: /**
084: * Type of transaction.
085: */
086: private TransactionManagementType transactionManagementType = null;
087:
088: /**
089: * Bean is using run-as ?
090: */
091: private boolean runAsBean = false;
092:
093: /**
094: * Link to the factory.
095: */
096: private Factory easyBeansFactory = null;
097:
098: /**
099: * Timer service.
100: */
101: private TimerService timerService = null;
102:
103: /**
104: * Builds a default EJB Context implementation.
105: * @param easyBeansFactory used to get the transaction management type.
106: */
107: public EasyBeansEJBContext(final Factory easyBeansFactory) {
108: this .easyBeansFactory = easyBeansFactory;
109: this .transactionManagementType = easyBeansFactory.getBeanInfo()
110: .getTransactionManagementType();
111: this .runAsBean = easyBeansFactory.getBeanInfo()
112: .getSecurityInfo().getRunAsRole() != null;
113:
114: this .transactionManager = JTransactionManager
115: .getTransactionManager();
116:
117: this .timerService = easyBeansFactory.getTimerService();
118: // If the component is not here, use a dummy implementation
119: if (timerService == null) {
120: timerService = new MissingTimerService();
121: }
122: }
123:
124: /**
125: * Obtain the enterprise bean's remote home interface.
126: * @return The enterprise bean's remote home interface.
127: * @throws IllegalStateException if the enterprise bean does not have a
128: * remote home interface.
129: */
130: public EJBHome getEJBHome() throws IllegalStateException {
131: throw new IllegalStateException("No Home");
132: }
133:
134: /**
135: * Obtain the enterprise bean's local home interface.
136: * @return The enterprise bean's local home interface.
137: * @throws IllegalStateException - if the enterprise bean does not have a
138: * local home interface.
139: */
140: public EJBLocalHome getEJBLocalHome() throws IllegalStateException {
141: throw new IllegalStateException("No Local Home");
142: }
143:
144: /**
145: * Use the JNDI naming context java:comp/env to access enterprise bean's
146: * environment. Obtain the enterprise bean's environment properties. Note:
147: * If the enterprise bean has no environment properties this method returns
148: * an empty java.util.Properties object. This method never returns null.
149: * @return The environment properties for the enterprise bean.
150: */
151: @Deprecated
152: public Properties getEnvironment() {
153: throw new UnsupportedOperationException();
154: }
155:
156: /**
157: * Use Principal getCallerPrincipal() instead. Obtain the
158: * java.security.Identity of the caller. This method is deprecated in EJB
159: * 1.1. The Container is allowed to return alway null from this method. The
160: * enterprise bean should use the getCallerPrincipal method instead.
161: * @return The Identity object that identifies the caller.
162: */
163: @Deprecated
164: public Identity getCallerIdentity() {
165: throw new UnsupportedOperationException();
166: }
167:
168: /**
169: * Obtain the java.security.Principal that identifies the caller.
170: * @return The Principal object that identifies the caller. This method
171: * never returns null.
172: */
173: public Principal getCallerPrincipal() {
174: return SecurityCurrent.getCurrent().getSecurityContext()
175: .getCallerPrincipal(runAsBean);
176: }
177:
178: /**
179: * Use boolean isCallerInRole(String roleName) instead. Test if the caller
180: * has a given role. This method is deprecated in EJB 1.1. The enterprise
181: * bean should use the isCallerInRole(String roleName) method instead.
182: * @param role The java.security.Identity of the role to be tested.
183: * @return True if the caller has the specified role.
184: */
185: @Deprecated
186: public boolean isCallerInRole(final Identity role) {
187: throw new UnsupportedOperationException();
188: }
189:
190: /**
191: * Test if the caller has a given security role.
192: * @param roleName The name of the security role. The role must be one of
193: * the security roles that is defined in the deployment descriptor.
194: * @return True if the caller has the specified role.
195: */
196: public boolean isCallerInRole(final String roleName) {
197:
198: // Get list of declared roles for this bean.
199: List<String> declaredRoles = easyBeansFactory.getBeanInfo()
200: .getSecurityInfo().getDeclaredRoles();
201: if (declaredRoles == null || !declaredRoles.contains(roleName)) {
202: logger
203: .debug(
204: "No security-role with role name ''{0}'' was declared for the bean ''{1}''",
205: roleName, easyBeansFactory.getBeanInfo()
206: .getName());
207: return false;
208: }
209:
210: // Check with JACC manager
211: boolean inRole = getBean().getEasyBeansFactory().getContainer()
212: .getPermissionManager().isCallerInRole(
213: easyBeansFactory.getBeanInfo().getName(),
214: roleName, runAsBean);
215:
216: return inRole;
217: }
218:
219: /**
220: * Obtain the transaction demarcation interface. Only enterprise beans with
221: * bean-managed transactions are allowed to to use the UserTransaction
222: * interface. As entity beans must always use container-managed
223: * transactions, only session beans with bean-managed transactions are
224: * allowed to invoke this method.
225: * @return The UserTransaction interface that the enterprise bean instance
226: * can use for transaction demarcation.
227: * @throws java.lang.IllegalStateException - The Container throws the
228: * exception if the instance is not allowed to use the
229: * UserTransaction interface (i.e. the instance is of a bean with
230: * container-managed transactions).
231: */
232: public UserTransaction getUserTransaction()
233: throws IllegalStateException {
234: if (transactionManagementType == CONTAINER) {
235: throw new IllegalStateException(
236: "This bean is not allowed to use getUserTransaction() "
237: + " method as it is in ContainerManagedTransaction");
238: }
239: return (UserTransaction) transactionManager;
240: }
241:
242: /**
243: * Mark the current transaction for rollback. The transaction will become
244: * permanently marked for rollback. A transaction marked for rollback can
245: * never commit. Only enterprise beans with container-managed transactions
246: * are allowed to use this method.
247: * @throws java.lang.IllegalStateException - The Container throws the
248: * exception if the instance is not allowed to use this method (i.e.
249: * the instance is of a bean with bean-managed transactions).
250: */
251: public void setRollbackOnly() throws IllegalStateException {
252: if (transactionManagementType == BEAN) {
253: throw new IllegalStateException(
254: "This bean is not allowed to use setRollbackOnly() "
255: + " method as it is in BeanManagedTransaction");
256: }
257:
258: // Check if there is a transaction, as it is mandatory
259: try {
260: if (transactionManager.getTransaction() == null) {
261: throw new IllegalStateException(
262: "Cannot use setRollbackOnly() outside transaction");
263: }
264: } catch (SystemException e) {
265: throw new IllegalStateException(
266: "Cannot get transaction on transaction manager", e);
267: }
268:
269: try {
270: transactionManager.setRollbackOnly();
271: } catch (SystemException e) {
272: throw new RuntimeException(
273: "setRollbackOnly() raised an unexpected exception:",
274: e);
275: }
276: }
277:
278: /**
279: * Test if the transaction has been marked for rollback only. An enterprise
280: * bean instance can use this operation, for example, to test after an
281: * exception has been caught, whether it is fruitless to continue
282: * computation on behalf of the current transaction. Only enterprise beans
283: * with container-managed transactions are allowed to use this method.
284: * @return True if the current transaction is marked for rollback, false
285: * otherwise.
286: * @throws java.lang.IllegalStateException - The Container throws the
287: * exception if the instance is not allowed to use this method (i.e.
288: * the instance is of a bean with bean-managed transactions).
289: */
290: public boolean getRollbackOnly() throws IllegalStateException {
291: if (transactionManagementType == BEAN) {
292: throw new IllegalStateException(
293: "This bean is not allowed to use getRollbackOnly() "
294: + " method as it is in BeanManagedTransaction");
295: }
296: try {
297: switch (transactionManager.getStatus()) {
298: case Status.STATUS_MARKED_ROLLBACK:
299: case Status.STATUS_ROLLING_BACK:
300: return true;
301: case Status.STATUS_ACTIVE:
302: case Status.STATUS_COMMITTING:
303: case Status.STATUS_PREPARED:
304: case Status.STATUS_PREPARING:
305: return false;
306: case Status.STATUS_ROLLEDBACK:
307: throw new IllegalStateException(
308: "Transaction already rolled back");
309: case Status.STATUS_COMMITTED:
310: throw new IllegalStateException(
311: "Transaction already committed");
312: case Status.STATUS_NO_TRANSACTION:
313: case Status.STATUS_UNKNOWN:
314: throw new IllegalStateException(
315: "Cannot getRollbackOnly outside transaction");
316: default:
317: throw new IllegalStateException("Invalid status");
318: }
319: } catch (SystemException e) {
320: throw new IllegalStateException(
321: "Cannot get transaction status", e);
322: }
323: }
324:
325: /**
326: * Get access to the EJB Timer Service.
327: * @return Timer service.
328: * @throws java.lang.IllegalStateException The Container throws the
329: * exception if the instance is not allowed to use this method (e.g.
330: * if the bean is a stateful session bean)
331: */
332: public TimerService getTimerService() throws IllegalStateException {
333: return timerService;
334: }
335:
336: /**
337: * @return string representation.
338: */
339: @Override
340: public String toString() {
341: StringBuilder sb = new StringBuilder();
342: // classname
343: sb.append(this .getClass().getName().substring(
344: this .getClass().getPackage().getName().length() + 1));
345: return sb.toString();
346: }
347:
348: /**
349: * Lookup object with given name.
350: * @param name given name
351: * @return result of the lookup
352: */
353: public Object lookup(final String name) {
354: // Search in java:comp/env first
355: try {
356: return new InitialContext().lookup(JAVA_COMP_ENV + name);
357: } catch (NamingException ne) {
358: // try in registry
359: try {
360: return new InitialContext().lookup(name);
361: } catch (NamingException e) {
362: throw new IllegalArgumentException("Lookup on '" + name
363: + "' was not found");
364: }
365: }
366: }
367:
368: /**
369: * Gets the bean of this context.
370: * @return bean used by this context.
371: */
372: public BeanType getBean() {
373: return null;
374: }
375:
376: /**
377: * Implementation of Timer Service that throws exception as
378: * it means that the timer component is missing.
379: * @author Florent Benoit
380: */
381: class MissingTimerService implements TimerService {
382:
383: /**
384: * Create an interval timer whose first expiration occurs after a
385: * specified duration, and whose subsequent expirations occur after a
386: * specified interval.
387: * @param initialDuration - The number of milliseconds that must elapse
388: * before the first timer expiration notification.
389: * @param intervalDuration - The number of milliseconds that must elapse
390: * between timer expiration notifications. Expiration
391: * notifications are scheduled relative to the time of the first
392: * expiration. If expiration is delayed(e.g. due to the
393: * interleaving of other method calls on the bean) two or more
394: * expiration notifications may occur in close succession to
395: * "catch up".
396: * @param info - Application information to be delivered along with the
397: * timer expiration. This can be null.
398: * @return The newly created Timer.
399: * @throws IllegalArgumentException - If initialDuration is negative, or
400: * intervalDuration is negative.
401: * @throws IllegalStateException - If this method is invoked while the
402: * instance is in a state that does not allow access to this
403: * method.
404: * @throws EJBException - If this method could not complete due to a
405: * system-level failure.
406: */
407: public Timer createTimer(final Date initialDuration,
408: final long intervalDuration, final Serializable info)
409: throws IllegalArgumentException, IllegalStateException,
410: EJBException {
411: throw new IllegalStateException(
412: "No timer component was found in the EasyBeans components");
413: }
414:
415: /**
416: * Create a single-action timer that expires at a given point in time.
417: * @param expiration - The point in time at which the timer must expire.
418: * @param info - Application information to be delivered along with the
419: * timer expiration notification. This can be null.
420: * @return The newly created Timer.
421: * @throws IllegalArgumentException - If expiration is null, or
422: * expiration.getTime() is negative.
423: * @throws IllegalStateException - If this method is invoked while the
424: * instance is in a state that does not allow access to this
425: * method.
426: * @throws EJBException - If this method could not complete due to a
427: * system-level failure.
428: */
429: public Timer createTimer(final Date expiration,
430: final Serializable info)
431: throws IllegalArgumentException, IllegalStateException,
432: EJBException {
433: throw new IllegalStateException(
434: "No timer component was found in the EasyBeans components");
435: }
436:
437: /**
438: * Create an interval timer whose first expiration occurs after a
439: * specified duration, and whose subsequent expirations occur after a
440: * specified interval.
441: * @param initialDuration - The number of milliseconds that must elapse
442: * before the first timer expiration notification.
443: * @param intervalDuration - The number of milliseconds that must elapse
444: * between timer expiration notifications. Expiration
445: * notifications are scheduled relative to the time of the first
446: * expiration. If expiration is delayed(e.g. due to the
447: * interleaving of other method calls on the bean) two or more
448: * expiration notifications may occur in close succession to
449: * "catch up".
450: * @param info - Application information to be delivered along with the
451: * timer expiration. This can be null.
452: * @return The newly created Timer.
453: * @throws IllegalArgumentException - If initialDuration is negative, or
454: * intervalDuration is negative.
455: * @throws IllegalStateException - If this method is invoked while the
456: * instance is in a state that does not allow access to this
457: * method.
458: * @throws EJBException - If this method could not complete due to a
459: * system-level failure.
460: */
461: public Timer createTimer(final long initialDuration,
462: final long intervalDuration, final Serializable info)
463: throws IllegalArgumentException, IllegalStateException,
464: EJBException {
465: throw new IllegalStateException(
466: "No timer component was found in the EasyBeans components");
467: }
468:
469: /**
470: * Create a single-action timer that expires after a specified duration.
471: * @param duration - The number of milliseconds that must elapse before
472: * the timer expires.
473: * @param info - Application information to be delivered along with the
474: * timer expiration notification. This can be null.
475: * @return The newly created Timer.
476: * @throws IllegalArgumentException - If duration is negative
477: * @throws IllegalStateException - If this method is invoked while the
478: * instance is in a state that does not allow access to this
479: * method.
480: * @throws EJBException - If this method fails due to a system-level
481: * failure.
482: */
483: public Timer createTimer(final long duration,
484: final Serializable info)
485: throws IllegalArgumentException, IllegalStateException,
486: EJBException {
487: throw new IllegalStateException(
488: "No timer component was found in the EasyBeans components");
489: }
490:
491: /**
492: * Get all the active timers associated with this bean.
493: * @return A collection of javax.ejb.Timer objects.
494: * @throws IllegalStateException - If this method is invoked while the
495: * instance is in a state that does not allow access to this
496: * method.
497: * @throws EJBException - If this method could not complete due to a
498: * system-level failure.
499: */
500: public Collection getTimers() throws IllegalStateException,
501: EJBException {
502: throw new IllegalStateException(
503: "No timer component was found in the EasyBeans components");
504: }
505:
506: }
507: }
|