001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.ejb.cfg;
031:
032: import com.caucho.ejb.cfg21.EjbHomeView;
033: import com.caucho.config.program.ContainerProgram;
034: import com.caucho.config.ConfigException;
035: import com.caucho.config.LineConfigException;
036: import com.caucho.config.types.EnvEntry;
037: import com.caucho.ejb.AbstractServer;
038: import com.caucho.ejb.gen.BeanGenerator;
039: import com.caucho.ejb.gen21.BeanAssembler;
040: import com.caucho.ejb.gen.SessionGenerator;
041: import com.caucho.ejb.gen.StatefulGenerator;
042: import com.caucho.ejb.gen.StatelessGenerator;
043: import com.caucho.ejb.manager.EjbContainer;
044: import com.caucho.ejb.session.StatefulServer;
045: import com.caucho.ejb.session.StatelessServer;
046: import com.caucho.java.gen.JavaClassGenerator;
047: import com.caucho.util.L10N;
048:
049: import javax.annotation.PostConstruct;
050: import javax.ejb.*;
051: import java.util.ArrayList;
052: import java.lang.reflect.*;
053:
054: /**
055: * Configuration for an ejb entity bean.
056: */
057: public class EjbSessionBean extends EjbBean {
058: private static final L10N L = new L10N(EjbSessionBean.class);
059:
060: private boolean _isStateless;
061:
062: // Default is container managed transaction.
063: private boolean _isContainerTransaction = true;
064:
065: private SessionGenerator _sessionBean;
066:
067: /**
068: * Creates a new session bean configuration.
069: */
070: public EjbSessionBean(EjbConfig ejbConfig, String ejbModuleName) {
071: super (ejbConfig, ejbModuleName);
072: }
073:
074: /**
075: * Returns the kind of bean.
076: */
077: public String getEJBKind() {
078: return "session";
079: }
080:
081: /**
082: * Sets the ejb implementation class.
083: */
084: @Override
085: public void setEJBClass(Class type) throws ConfigException {
086: super .setEJBClass(type);
087:
088: ApiClass ejbClass = getEJBClassWrapper();
089:
090: if (ejbClass.isAbstract())
091: throw error(L
092: .l(
093: "'{0}' must not be abstract. Session bean implementations must be fully implemented.",
094: ejbClass.getName()));
095:
096: if (type.isAnnotationPresent(Stateless.class)) {
097: Stateless stateless = (Stateless) type
098: .getAnnotation(Stateless.class);
099:
100: if (getEJBName() == null && !"".equals(stateless.name()))
101: setEJBName(stateless.name());
102:
103: _isStateless = true;
104: } else if (ejbClass.isAnnotationPresent(Stateful.class)) {
105: Stateful stateful = (Stateful) type
106: .getAnnotation(Stateful.class);
107:
108: if (getEJBName() == null && !"".equals(stateful.name()))
109: setEJBName(stateful.name());
110:
111: _isStateless = false;
112: }
113:
114: if (getEJBName() == null)
115: setEJBName(ejbClass.getSimpleName());
116:
117: /*
118: if (! ejbClass.isAssignableTo(SessionBean.class)
119: && ! ejbClass.isAnnotationPresent(Stateless.class)
120: && ! ejbClass.isAnnotationPresent(Stateful.class))
121: throw error(L.l("'{0}' must implement SessionBean or @Stateless or @Stateful. Session beans must implement javax.ejb.SessionBean.", ejbClass.getName()));
122: */
123:
124: // introspectSession();
125: }
126:
127: /**
128: * Returns true if it's a stateless session bean.
129: */
130: public boolean isStateless() {
131: return _isStateless;
132: }
133:
134: /**
135: * Set true if it's a stateless session bean.
136: */
137: public void setSessionType(String type) throws ConfigException {
138: if (type.equals("Stateful"))
139: _isStateless = false;
140: else if (type.equals("Stateless"))
141: _isStateless = true;
142: else
143: throw new ConfigException(
144: L
145: .l(
146: "'{0}' is an unknown session-type. session-type must be 'Stateless' or 'Stateful'.",
147: type));
148: }
149:
150: /**
151: * Returns true if the container handles transactions.
152: */
153: public boolean isContainerTransaction() {
154: return _isContainerTransaction;
155: }
156:
157: /**
158: * Set true if the container handles transactions.
159: */
160: public void setTransactionType(String type) throws ConfigException {
161: if (type.equals("Container"))
162: _isContainerTransaction = true;
163: else if (type.equals("Bean"))
164: _isContainerTransaction = false;
165: else
166: throw new ConfigException(
167: L
168: .l(
169: "'{0}' is an unknown transaction-type. transaction-type must be 'Container' or 'Bean'.",
170: type));
171: }
172:
173: /**
174: * Configure initialization.
175: */
176: @PostConstruct
177: public void init() throws ConfigException {
178: super .init();
179:
180: try {
181: if (getRemoteHome() != null) {
182: validateHome(getRemoteHome(), getRemoteList().get(0));
183: }
184:
185: if (getLocalHome() != null) {
186: validateHome(getLocalHome(), getLocalList().get(0));
187: }
188:
189: for (ApiClass remoteApi : getRemoteList())
190: validateRemote(remoteApi);
191:
192: for (ApiClass localApi : getLocalList())
193: validateRemote(localApi);
194:
195: if (getEJBClass() == null) {
196: throw error(L
197: .l(
198: "'{0}' does not have a defined ejb-class. Session beans must have an ejb-class.",
199: getEJBName()));
200: }
201:
202: if (!SessionSynchronization.class
203: .isAssignableFrom(getEJBClassWrapper()
204: .getJavaClass())) {
205: } else if (isStateless()) {
206: throw error(L
207: .l(
208: "'{0}' must not implement SessionSynchronization. Stateless session beans must not implement SessionSynchronization.",
209: getEJBClass().getName()));
210: } else if (!_isContainerTransaction) {
211: throw error(L
212: .l(
213: "'{0}' must not implement SessionSynchronization. Session beans with Bean-managed transactions may not use SessionSynchronization.",
214: getEJBClass().getName()));
215: }
216: } catch (LineConfigException e) {
217: throw e;
218: } catch (ConfigException e) {
219: throw new LineConfigException(getLocation()
220: + e.getMessage(), e);
221: }
222:
223: /*
224: if (isStateless())
225: J2EEManagedObject.register(new StatelessSessionBean(this));
226: else
227: J2EEManagedObject.register(new StatefulSessionBean(this));
228: */
229: }
230:
231: /**
232: * Creates the bean generator for the session bean.
233: */
234: @Override
235: protected BeanGenerator createBeanGenerator() {
236: if (_isStateless)
237: _sessionBean = new StatelessGenerator(getEJBName(),
238: getEJBClassWrapper());
239: else
240: _sessionBean = new StatefulGenerator(getEJBName(),
241: getEJBClassWrapper());
242:
243: return _sessionBean;
244: }
245:
246: /**
247: * Obtain and apply initialization from annotations.
248: */
249: public void initIntrospect() throws ConfigException {
250: super .initIntrospect();
251:
252: ApiClass type = getEJBClassWrapper();
253:
254: // XXX: ejb/0f78
255: if (type == null)
256: return;
257:
258: // ejb/0j20
259: if (!type.isAnnotationPresent(Stateful.class)
260: && !type.isAnnotationPresent(Stateless.class)
261: && !isAllowPOJO())
262: return;
263:
264: /* TCK: ejb/0f6d: bean with local and remote interfaces
265: if (_localHome != null || _localList.size() != 0
266: || _remoteHome != null || _remoteList.size() != 0)
267: return;
268: */
269:
270: Class[] ifs = type.getInterfaces();
271:
272: ArrayList<ApiClass> interfaceList = new ArrayList<ApiClass>();
273:
274: for (int i = 0; i < ifs.length; i++) {
275: ApiClass localApi = new ApiClass(ifs[i]);
276:
277: Local local = (Local) ifs[i].getAnnotation(Local.class);
278:
279: if (local != null) {
280: setLocalWrapper(localApi);
281: continue;
282: }
283:
284: javax.ejb.Remote remote = (javax.ejb.Remote) ifs[i]
285: .getAnnotation(javax.ejb.Remote.class);
286:
287: if (remote != null
288: || java.rmi.Remote.class.isAssignableFrom(ifs[i])) {
289: setRemoteWrapper(localApi);
290: continue;
291: }
292:
293: if (ifs[i].getName().equals("java.io.Serializable"))
294: continue;
295:
296: if (ifs[i].getName().equals("java.io.Externalizable"))
297: continue;
298:
299: if (ifs[i].getName().startsWith("javax.ejb"))
300: continue;
301:
302: if (ifs[i].getName().equals("java.rmi.Remote"))
303: continue;
304:
305: if (!interfaceList.contains(localApi))
306: interfaceList.add(localApi);
307: }
308:
309: // if (getLocalList().size() != 0 || getRemoteList().size() != 0) {
310: if (_localHome != null || _localList.size() != 0
311: || _remoteHome != null || _remoteList.size() != 0) {
312: } else if (interfaceList.size() == 0)
313: throw new ConfigException(
314: L
315: .l(
316: "'{0}' has no interfaces. Can't currently generate.",
317: type.getName()));
318: else if (interfaceList.size() != 1)
319: throw new ConfigException(
320: L
321: .l(
322: "'{0}' has multiple interfaces, but none are marked as @Local or @Remote.\n{1}",
323: type.getName(), interfaceList
324: .toString()));
325: else {
326: setLocalWrapper(interfaceList.get(0));
327: }
328:
329: // XXX: Check ejb30/bb/session/stateless/migration/twothree/annotated
330: // There is a conflict between 2.1 and 3.0 interfaces.
331:
332: // ejb/0f6f
333: // The session bean might have @RemoteHome for EJB 2.1 and
334: // the @Remote interface for EJB 3.0 (same with @LocalHome and @Local).
335: // TCK: ejb30/bb/session/stateful/sessioncontext/annotated
336:
337: ApiClass ejbClass = getEJBClassWrapper();
338:
339: LocalHome localHomeAnn = ejbClass
340: .getAnnotation(LocalHome.class);
341:
342: // ejb/0f6f
343: if (localHomeAnn != null) {
344: Class localHome = localHomeAnn.value();
345: setLocalHome(localHome);
346: }
347:
348: RemoteHome remoteHomeAnn = ejbClass
349: .getAnnotation(RemoteHome.class);
350:
351: // ejb/0f6f
352: if (remoteHomeAnn != null) {
353: Class home = remoteHomeAnn.value();
354: setHome(home);
355: }
356: }
357:
358: /**
359: * Creates the assembler for the bean.
360: */
361: protected BeanAssembler createAssembler(String fullClassName) {
362: throw new IllegalStateException(getClass().getName());
363: /*
364: if (isStateless())
365: return new StatelessAssembler(this, fullClassName);
366: else
367: return new SessionAssembler(this, fullClassName);
368: */
369: }
370:
371: /**
372: * Adds the assemblers.
373: */
374: protected void addImports(BeanAssembler assembler) {
375: super .addImports(assembler);
376:
377: if (isStateless()) {
378: assembler
379: .addImport("com.caucho.ejb.session.StatelessServer");
380: assembler
381: .addImport("com.caucho.ejb.session.AbstractStatelessContext");
382: assembler.addImport("com.caucho.ejb.session.StatelessHome");
383: assembler
384: .addImport("com.caucho.ejb.session.StatelessObject21");
385: assembler
386: .addImport("com.caucho.ejb.session.StatelessObject");
387: } else {
388: assembler.addImport("com.caucho.ejb.session.SessionServer");
389: assembler
390: .addImport("com.caucho.ejb.session.AbstractSessionContext");
391: assembler.addImport("com.caucho.ejb.session.SessionHome");
392: assembler
393: .addImport("com.caucho.ejb.session.SessionObject21");
394: assembler.addImport("com.caucho.ejb.session.SessionObject");
395: }
396: }
397:
398: /**
399: * Creates the views.
400: */
401: protected EjbHomeView createHomeView(ApiClass homeClass,
402: String prefix) throws ConfigException {
403: if (isStateless())
404: return new EjbStatelessHomeView(this , homeClass, prefix);
405: else
406: return new EjbSessionHomeView(this , homeClass, prefix);
407: }
408:
409: /**
410: * Deploys the bean.
411: */
412: @Override
413: public AbstractServer deployServer(EjbContainer ejbContainer,
414: JavaClassGenerator javaGen) throws ClassNotFoundException,
415: ConfigException {
416: AbstractServer server;
417:
418: if (isStateless())
419: server = new StatelessServer(ejbContainer);
420: else
421: server = new StatefulServer(ejbContainer);
422:
423: server.setModuleName(getEJBModuleName());
424: server.setEJBName(getEJBName());
425: server.setMappedName(getMappedName());
426: server.setId(getEJBModuleName() + "#" + getEJBName());
427: server.setContainerTransaction(_isContainerTransaction);
428:
429: server.setEjbClass(loadClass(getEJBClass().getName()));
430:
431: ApiClass remoteHome = getRemoteHome();
432: if (remoteHome != null)
433: server.setRemoteHomeClass(loadClass(remoteHome.getName()));
434:
435: ArrayList<ApiClass> remoteList = _sessionBean.getRemoteApi();
436: if (remoteList.size() > 0) {
437: ArrayList<Class> classList = new ArrayList<Class>();
438: for (ApiClass apiClass : remoteList) {
439: classList.add(loadClass(apiClass.getName()));
440: }
441:
442: server.setRemoteApiList(classList);
443: }
444:
445: /*
446: if (getRemote21() != null)
447: server.setRemote21(loadClass(getRemote21().getName()));
448: */
449:
450: ApiClass localHome = getLocalHome();
451: if (localHome != null)
452: server.setLocalHomeClass(loadClass(localHome.getName()));
453:
454: ArrayList<ApiClass> localList = _sessionBean.getLocalApi();
455: if (localList.size() > 0) {
456: ArrayList<Class> classList = new ArrayList<Class>();
457: for (ApiClass apiClass : localList) {
458: classList.add(loadClass(apiClass.getName()));
459: }
460:
461: server.setLocalApiList(classList);
462: }
463:
464: /*
465: if (getLocal21() != null)
466: server.setLocal21(loadClass(getLocal21().getName()));
467: */
468:
469: Class contextImplClass = javaGen.loadClass(getSkeletonName());
470:
471: server.setContextImplClass(contextImplClass);
472:
473: Class beanClass = javaGen.loadClass(getEJBClass().getName());
474:
475: Thread thread = Thread.currentThread();
476: ClassLoader oldLoader = thread.getContextClassLoader();
477:
478: try {
479: thread.setContextClassLoader(server.getClassLoader());
480:
481: ContainerProgram initContainer = getInitProgram();
482:
483: server.setInitProgram(initContainer);
484:
485: try {
486: if (getServerProgram() != null)
487: getServerProgram().configure(server);
488: } catch (RuntimeException e) {
489: throw e;
490: } catch (Exception e) {
491: throw ConfigException.create(e);
492: }
493:
494: } finally {
495: thread.setContextClassLoader(oldLoader);
496: }
497:
498: return server;
499: }
500:
501: private void introspectSession() throws ConfigException {
502: ApiClass ejbClass = getEJBClassWrapper();
503:
504: if (ejbClass.isAnnotationPresent(Stateless.class))
505: introspectStateless(ejbClass);
506: else if (ejbClass.isAnnotationPresent(Stateful.class))
507: introspectStateful(ejbClass);
508: }
509:
510: private void introspectStateless(ApiClass type)
511: throws ConfigException {
512: String className = type.getName();
513:
514: Stateless stateless = type.getAnnotation(Stateless.class);
515:
516: setAllowPOJO(true);
517:
518: setSessionType("Stateless");
519:
520: setTransactionType(type);
521:
522: String name;
523: if (stateless != null)
524: name = stateless.name();
525: else
526: name = className;
527:
528: introspectBean(type, name);
529: }
530:
531: private void introspectStateful(ApiClass type)
532: throws ConfigException {
533: String className = type.getName();
534:
535: Stateful stateful = type.getAnnotation(Stateful.class);
536:
537: setAllowPOJO(true);
538:
539: setSessionType("Stateful");
540:
541: setTransactionType(type);
542:
543: String name;
544: if (stateful != null)
545: name = stateful.name();
546: else
547: name = className;
548:
549: introspectBean(type, name);
550: }
551:
552: private void setTransactionType(ApiClass type) {
553: TransactionManagement transaction = type
554: .getAnnotation(TransactionManagement.class);
555:
556: if (transaction == null)
557: setTransactionType("Container");
558: else if (TransactionManagementType.BEAN.equals(transaction
559: .value()))
560: setTransactionType("Bean");
561: else
562: setTransactionType("Container");
563: }
564:
565: private void validateMethods() throws ConfigException {
566: }
567:
568: /**
569: * Validates the home interface.
570: */
571: private void validateHome(ApiClass homeClass, ApiClass objectClass)
572: throws ConfigException {
573: ApiClass beanClass = getEJBClassWrapper();
574: String beanName = beanClass.getName();
575:
576: if (homeClass == null)
577: return;
578: String homeName = homeClass.getName();
579: String objectName = objectClass.getName();
580:
581: boolean hasFindByPrimaryKey = false;
582:
583: if (!homeClass.isPublic())
584: throw error(L.l("'{0}' must be public", homeName));
585:
586: if (beanClass.isFinal())
587: throw error(L.l("'{0}' must not be final", beanName));
588:
589: if (beanClass.isAbstract())
590: throw error(L.l("'{0}' must not be abstract", beanName));
591:
592: if (!homeClass.isInterface())
593: throw error(L.l("'{0}' must be an interface", homeName));
594:
595: boolean hasCreate = false;
596:
597: for (ApiMethod method : homeClass.getMethods()) {
598: String name = method.getName();
599: Class[] param = method.getParameterTypes();
600: Class retType = method.getReturnType();
601:
602: if (method.getDeclaringClass().isAssignableFrom(
603: EJBHome.class)
604: || method.getDeclaringClass().isAssignableFrom(
605: EJBLocalHome.class))
606: continue;
607:
608: if (EJBHome.class
609: .isAssignableFrom(homeClass.getJavaClass()))
610: validateException(method,
611: java.rmi.RemoteException.class);
612:
613: if (name.startsWith("create")) {
614: hasCreate = true;
615:
616: // TCK: ejb30/bb/session/stateless/migration/twothree/descriptor/callLocalSameTxContextTest
617: // XXX TCK, needs QA: if a stateless session bean has EJB 3.0 and 2.1 interfaces,
618: // it is not required to have a matching ejbCreate().
619: // It may have a @PostConstruct method, if any.
620: if (isStateless() && name.equals("create"))
621: continue;
622:
623: if (isStateless()
624: && (!name.equals("create") || method
625: .getParameterTypes().length != 0)) {
626: throw error(L
627: .l(
628: "{0}: '{1}' forbidden in stateless session home. The create() method for a stateless session bean must have zero arguments.",
629: method.getFullName(), homeName));
630: }
631:
632: if (!isAllowPOJO())
633: validateException(method, CreateException.class);
634:
635: /* XXX: ejb/0f6f
636:
637: The session bean might have @RemoteHome for EJB 2.1 and the @Remote interface for EJB 3.0
638: TCK: ejb30/bb/session/stateful/sessioncontext/annotated
639:
640: if (! retType.equals(objectClass))
641: throw error(L.l("{0}: '{1}' must return {2}. Create methods must return the local or remote interface.",
642: homeName,
643: method.getFullName(),
644: objectClass.getName()));
645: */
646:
647: String createName = "ejbC" + name.substring(1);
648: ApiMethod implMethod = validateNonFinalMethod(
649: createName, param, method, homeClass,
650: isAllowPOJO());
651:
652: if (implMethod != null) {
653: if (!implMethod.getReturnType().getName().equals(
654: "void"))
655: throw error(L.l("'{0}' must return {1} in {2}",
656: getFullMethodName(createName, param),
657: "void", beanName));
658:
659: validateExceptions(method, implMethod
660: .getExceptionTypes());
661: }
662: } else if (name.startsWith("ejb")
663: || name.startsWith("remove")) {
664: throw error(L.l("'{0}' forbidden in {1}", method
665: .getFullName(), homeClass.getName()));
666: }
667: }
668:
669: if (!hasCreate)
670: throw error(L
671: .l(
672: "'{0}' needs at least one create method. Session beans need a create method.",
673: homeClass.getName()));
674: }
675: }
|