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.config.ConfigException;
033: import com.caucho.config.program.ContainerProgram;
034: import com.caucho.config.types.JndiBuilder;
035: import com.caucho.ejb.AbstractServer;
036: import com.caucho.ejb.gen.*;
037: import com.caucho.ejb.manager.EjbContainer;
038: import com.caucho.ejb.message.*;
039: import com.caucho.java.gen.JavaClassGenerator;
040: import com.caucho.jca.*;
041: import com.caucho.jca.cfg.*;
042: import com.caucho.util.L10N;
043: import com.caucho.webbeans.component.*;
044: import com.caucho.webbeans.manager.*;
045:
046: import javax.annotation.PostConstruct;
047: import javax.ejb.ActivationConfigProperty;
048: import javax.ejb.MessageDriven;
049: import javax.ejb.TransactionManagement;
050: import javax.ejb.TransactionManagementType;
051: import javax.interceptor.AroundInvoke;
052: import javax.jms.ConnectionFactory;
053: import javax.jms.Destination;
054: import javax.jms.MessageListener;
055: import javax.jms.Session;
056: import javax.resource.spi.*;
057: import javax.naming.NamingException;
058: import javax.webbeans.*;
059: import java.lang.reflect.*;
060: import java.util.logging.Level;
061: import java.util.logging.Logger;
062:
063: /**
064: * Configuration for an ejb entity bean.
065: */
066: public class EjbMessageBean extends EjbBean {
067: private static final Logger log = Logger
068: .getLogger(EjbMessageBean.class.getName());
069: private static final L10N L = new L10N(EjbMessageBean.class);
070:
071: private ConnectionFactory _connectionFactory;
072:
073: private ActivationSpec _activationSpec;
074: private Destination _destination;
075: private String _messageSelector;
076: private int _acknowledgeMode = Session.AUTO_ACKNOWLEDGE;
077: private String _selector;
078: private String _subscriptionName;
079: private int _consumerMax = -1;
080: private String _messageDestinationLink;
081: private Class _messagingType;
082:
083: private MessageGenerator _messageBean;
084:
085: /**
086: * Creates a new message bean configuration.
087: */
088: public EjbMessageBean(EjbConfig config, String ejbModuleName) {
089: super (config, ejbModuleName);
090: }
091:
092: /**
093: * Returns the kind of bean.
094: */
095: @Override
096: public String getEJBKind() {
097: return "message";
098: }
099:
100: /**
101: * Sets the ejb implementation class.
102: */
103: @Override
104: public void setEJBClass(Class ejbClass) throws ConfigException {
105: super .setEJBClass(ejbClass);
106:
107: // ejb/0987
108: /*
109: if (! MessageDrivenBean.class.isAssignableFrom(ejbClass)
110: && ! isAllowPOJO())
111: throw error(L.l("'{0}' must implement javax.ejb.MessageDrivenBean. Every message-driven bean must implement MessageDrivenBean.", ejbClass.getName()));
112: */
113:
114: if (Modifier.isAbstract(ejbClass.getModifiers()))
115: throw error(L
116: .l(
117: "'{0}' must not be abstract. Every message-driven bean must be a fully-implemented class.",
118: ejbClass.getName()));
119:
120: // ejb 3.0 simplified section 10.1.3
121: // The name annotation element defaults to the unqualified name of the bean
122: // class.
123:
124: if (getEJBName() == null) {
125: setEJBName(ejbClass.getSimpleName());
126: }
127: }
128:
129: /**
130: * Creates the old EJB 2.0 message-driven-destination
131: */
132: public MessageDrivenDestination createMessageDrivenDestination() {
133: return new MessageDrivenDestination();
134: }
135:
136: /**
137: * Sets the JCA activation spec.
138: */
139: public void setActivationSpec(ActivationSpec activationSpec) {
140: _activationSpec = activationSpec;
141: }
142:
143: /**
144: * Sets the JMS destination.
145: */
146: public void setDestination(Destination destination)
147: throws ConfigException {
148: _destination = destination;
149: }
150:
151: /**
152: * Sets the JMS destination.
153: */
154: public void setDestinationValue(Destination destination) {
155: _destination = destination;
156: }
157:
158: public void setMessagingType(Class messagingType) {
159: if (messagingType != Object.class)
160: _messagingType = messagingType;
161: }
162:
163: /**
164: * Returns the destination.
165: */
166: public Destination getDestination() {
167: return _destination;
168: }
169:
170: /**
171: * @deprecated for compat with TCK
172: */
173: public void setMappedName(String mappedName) throws ConfigException {
174: // XXX:
175: // setDestination(destination);
176: }
177:
178: /**
179: * Sets the JMS destination type.
180: */
181: public void setMessageDestinationType(String type)
182: throws ConfigException, NamingException {
183: }
184:
185: /**
186: * Sets the JMS destination link
187: */
188: public void setMessageDestinationLink(String messageDestinationLink)
189: throws ConfigException, NamingException {
190: _messageDestinationLink = messageDestinationLink;
191: }
192:
193: /**
194: * Sets the connection factory.
195: */
196: public void setConnectionFactory(JndiBuilder factory)
197: throws ConfigException, NamingException {
198: if (!(factory.getObject() instanceof ConnectionFactory))
199: throw new ConfigException(
200: L
201: .l(
202: "'{0}' needs to implement javax.jms.ConnectionFactory.",
203: factory.getObject()));
204:
205: _connectionFactory = (ConnectionFactory) factory.getObject();
206: }
207:
208: /**
209: * Sets the connection factory.
210: */
211: public void setConnectionFactoryValue(ConnectionFactory factory) {
212: _connectionFactory = factory;
213: }
214:
215: /**
216: * Returns the destination.
217: */
218: public ConnectionFactory getConnectionFactory() {
219: return _connectionFactory;
220: }
221:
222: /**
223: * Returns the acknowledge mode.
224: */
225: public int getAcknowledgeMode() {
226: return _acknowledgeMode;
227: }
228:
229: /**
230: * Set the acknowledge mode.
231: */
232: public void setAcknowledgeMode(int acknowledgeMode) {
233: _acknowledgeMode = acknowledgeMode;
234: }
235:
236: /**
237: * Returns the message selector
238: */
239: public String getSelector() {
240: return _selector;
241: }
242:
243: /**
244: * Set the message selector.
245: */
246: public void setSelector(String selector) {
247: _selector = selector;
248: }
249:
250: /**
251: * Returns the durable subscription name
252: */
253: public String getSubscriptionName() {
254: return _subscriptionName;
255: }
256:
257: /**
258: * Set the message selector.
259: */
260: public void setSubscriptionName(String subscriptionName) {
261: _subscriptionName = subscriptionName;
262: }
263:
264: /**
265: * Set true if the container handles transactions.
266: */
267: public void setTransactionType(String type) throws ConfigException {
268: if (type.equals("Container")) {
269: setContainerTransaction(true);
270: } else if (type.equals("Bean")) {
271: setContainerTransaction(false);
272: } else
273: throw new ConfigException(
274: L
275: .l(
276: "'{0}' is an unknown transaction-type. transaction-type must be 'Bean' or 'Container'.",
277: type));
278: }
279:
280: public void setSecurityIdentity(SecurityIdentity identity) {
281: }
282:
283: /**
284: * Adds the activation config.
285: */
286: public ActivationConfig createActivationConfig() {
287: return new ActivationConfig();
288: }
289:
290: public void setResourceAdapter(String name) {
291: ResourceArchive ra = ResourceArchiveManager
292: .findResourceArchive(name);
293:
294: if (ra == null)
295: throw new ConfigException(L
296: .l("'{0}' is an unknown resource-adapter"));
297: }
298:
299: private void addActivationConfigProperty(String name, Object value) {
300: if ("destination".equals(name)) {
301: if (value instanceof Destination)
302: setDestination((Destination) value);
303: else {
304: WebBeansContainer webBeans = WebBeansContainer.create();
305:
306: Destination dest = webBeans.getObject(
307: Destination.class, String.valueOf(value));
308:
309: setDestination(dest);
310: }
311: } else if ("messageSelector".equals(name)) {
312: _messageSelector = (String) value;
313: } else
314: log
315: .log(
316: Level.FINE,
317: L
318: .l(
319: "activation-config-property '{0}' is unknown, ignored",
320: name));
321: }
322:
323: /**
324: * Sets the number of message consumers.
325: */
326: public void setMessageConsumerMax(int consumerMax)
327: throws ConfigException {
328: _consumerMax = consumerMax;
329: }
330:
331: /**
332: * Initialize
333: */
334: @PostConstruct
335: @Override
336: public void init() throws ConfigException {
337: if (_messagingType != null) {
338: } else if (_activationSpec != null) {
339: String specName = _activationSpec.getClass().getName();
340:
341: ResourceArchive ra = ResourceArchiveManager
342: .findResourceArchive(specName);
343:
344: if (ra == null) {
345: throw new ConfigException(
346: L
347: .l(
348: "'{0}' is an unknown activation-spec. Make sure the JCA adapter is deployed in a .rar file",
349: specName));
350: }
351:
352: try {
353: _activationSpec.validate();
354: } catch (Exception e) {
355: throw error(e);
356: }
357:
358: MessageListenerConfig listener = ra
359: .getMessageListener(specName);
360:
361: _messagingType = listener.getMessageListenerType();
362: } else if (MessageListener.class
363: .isAssignableFrom(getEJBClass())) {
364: _messagingType = MessageListener.class;
365: } else
366: throw error(L
367: .l(
368: "'{0}' must implement javax.jms.MessageListener or specify {1}.",
369: getEJBClass().getName(),
370: isAllowPOJO() ? "messaging-type"
371: : "messageListenerInterface"));
372:
373: super .init();
374:
375: ApiMethod ejbCreate = getEJBClassWrapper().getMethod(
376: "ejbCreate", new Class[0]);
377:
378: if (ejbCreate != null) {
379: if (!ejbCreate.isPublic() && !ejbCreate.isProtected())
380: throw error(L
381: .l(
382: "{0}: ejbCreate method must be public or protected.",
383: getEJBClass().getName()));
384: }
385:
386: // J2EEManagedObject.register(new com.caucho.management.j2ee.MessageDrivenBean(this));
387: }
388:
389: protected void introspect() {
390: _messageBean.setApi(new ApiClass(_messagingType));
391:
392: super .introspect();
393:
394: MessageDriven messageDriven = (MessageDriven) getEJBClass()
395: .getAnnotation(MessageDriven.class);
396:
397: if (messageDriven != null) {
398: ActivationConfigProperty[] activationConfig = messageDriven
399: .activationConfig();
400:
401: if (activationConfig != null) {
402: for (ActivationConfigProperty prop : activationConfig) {
403: addActivationConfigProperty(prop.propertyName(),
404: prop.propertyValue());
405:
406: }
407: }
408:
409: Class type = messageDriven.messageListenerInterface();
410: if (type != null && !Object.class.equals(type))
411: _messagingType = type;
412: }
413: }
414:
415: /**
416: * Creates the bean generator for the session bean.
417: */
418: @Override
419: protected BeanGenerator createBeanGenerator() {
420: _messageBean = new MessageGenerator(getEJBName(),
421: getEJBClassWrapper());
422:
423: return _messageBean;
424: }
425:
426: /**
427: * Obtain and apply initialization from annotations.
428: */
429: @Override
430: public void initIntrospect() throws ConfigException {
431: // ejb/0fbm
432: super .initIntrospect();
433:
434: ApiClass type = getEJBClassWrapper();
435:
436: // ejb/0j40
437: if (!type.isAnnotationPresent(MessageDriven.class)
438: && !type.isAnnotationPresent(MessageDriven.class)
439: && !isAllowPOJO())
440: return;
441:
442: // XXX: annotations in super classes?
443:
444: javax.ejb.MessageDriven messageDriven = type
445: .getAnnotation(javax.ejb.MessageDriven.class);
446:
447: if (messageDriven != null) {
448: ActivationConfigProperty[] properties = messageDriven
449: .activationConfig();
450:
451: if (properties != null) {
452: for (ActivationConfigProperty property : properties)
453: addActivationConfigProperty(
454: property.propertyName(), property
455: .propertyValue());
456: }
457:
458: Class messageListenerInterface = messageDriven
459: .messageListenerInterface();
460:
461: if (messageListenerInterface != null)
462: setMessagingType(messageListenerInterface);
463:
464: TransactionManagement transaction = type
465: .getAnnotation(TransactionManagement.class);
466: if (transaction == null)
467: setTransactionType("Container");
468: else if (TransactionManagementType.BEAN.equals(transaction
469: .value()))
470: setTransactionType("Bean");
471: else
472: setTransactionType("Container");
473:
474: configureMethods(type);
475: }
476: }
477:
478: private void configureMethods(ApiClass type) throws ConfigException {
479: for (ApiMethod method : type.getMethods()) {
480: AroundInvoke aroundInvoke = method
481: .getAnnotation(AroundInvoke.class);
482:
483: // ejb/0fbl
484: if (aroundInvoke != null) {
485: setAroundInvokeMethodName(method.getName());
486:
487: // XXX: needs to check invalid duplicated @AroundInvoke methods.
488: break;
489: }
490: }
491: }
492:
493: /**
494: * Deploys the bean.
495: */
496: @Override
497: public AbstractServer deployServer(EjbContainer ejbManager,
498: JavaClassGenerator javaGen) throws ClassNotFoundException {
499: if (_activationSpec != null)
500: return deployActivationSpecServer(ejbManager, javaGen);
501: else
502: return deployJmsServer(ejbManager, javaGen);
503: }
504:
505: private AbstractServer deployJmsServer(EjbContainer ejbManager,
506: JavaClassGenerator javaGen) throws ClassNotFoundException {
507: ConnectionFactory factory;
508: Destination destination = null;
509:
510: if (_connectionFactory != null)
511: factory = _connectionFactory;
512: else
513: factory = getEjbContainer().getJmsConnectionFactory();
514:
515: if (factory == null) {
516: WebBeansContainer webBeans = WebBeansContainer.create();
517:
518: factory = webBeans.getObject(ConnectionFactory.class);
519: }
520:
521: if (_destination != null)
522: destination = _destination;
523: else if (_messageDestinationLink != null) {
524: MessageDestination dest;
525: dest = getConfig().getMessageDestination(
526: _messageDestinationLink);
527:
528: if (dest != null)
529: destination = dest.getResolvedDestination();
530: }
531:
532: if (destination == null)
533: throw new ConfigException(
534: L
535: .l(
536: "ejb-message-bean '{0}' does not have a configured JMS destination or activation-spec",
537: getEJBName()));
538:
539: if (factory == null)
540: throw new ConfigException(
541: L
542: .l(
543: "ejb-message-bean '{0}' does not have a configured JMS connection factory",
544: getEJBName()));
545:
546: JmsResourceAdapter ra = new JmsResourceAdapter(getEJBName(),
547: factory, destination);
548:
549: JmsActivationSpec spec = new JmsActivationSpec();
550:
551: ra.setAcknowledgeMode(_acknowledgeMode);
552: ra.setMessageSelector(_messageSelector);
553: ra.setSubscriptionName(_subscriptionName);
554:
555: if (_consumerMax > 0)
556: ra.setConsumerMax(_consumerMax);
557: else
558: ra
559: .setConsumerMax(getEjbContainer()
560: .getMessageConsumerMax());
561:
562: return deployMessageServer(ejbManager, javaGen, ra, spec);
563: }
564:
565: /**
566: * Deploys the bean.
567: */
568: public AbstractServer deployActivationSpecServer(
569: EjbContainer ejbManager, JavaClassGenerator javaGen)
570: throws ClassNotFoundException {
571: if (_activationSpec == null)
572: throw new ConfigException(
573: L
574: .l("ActivationSpec is required for ActivationSpecServer"));
575:
576: String specType = _activationSpec.getClass().getName();
577:
578: ResourceArchive raCfg = ResourceArchiveManager
579: .findResourceArchive(specType);
580:
581: if (raCfg == null)
582: throw error(L
583: .l(
584: "'{0}' is an unknown activation-spec. Make sure the .rar file for the driver is properly installed.",
585: specType));
586:
587: Class raClass = raCfg.getResourceAdapterClass();
588:
589: if (raClass == null)
590: throw error(L
591: .l(
592: "resource-adapter class does not exist for activation-spec '{0}'. Make sure the .rar file for the driver is properly installed.",
593: raClass.getName()));
594:
595: WebBeansContainer webBeans = WebBeansContainer.create();
596:
597: ComponentFactory raFactory = webBeans.resolveByType(raClass);
598:
599: if (raFactory == null) {
600: throw error(L
601: .l(
602: "resource-adapter '{0}' must be configured in a <connector> tag.",
603: raClass.getName()));
604: }
605:
606: ResourceAdapter ra = (ResourceAdapter) raFactory.get();
607:
608: if (ra == null)
609: throw new NullPointerException();
610:
611: return deployMessageServer(ejbManager, javaGen, ra,
612: _activationSpec);
613: }
614:
615: /**
616: * Deploys the bean.
617: */
618: public AbstractServer deployMessageServer(EjbContainer ejbManager,
619: JavaClassGenerator javaGen, ResourceAdapter ra,
620: ActivationSpec spec) throws ClassNotFoundException {
621: MessageServer server;
622:
623: try {
624: if (spec == null)
625: throw new ConfigException(
626: L
627: .l("ActivationSpec is required for MessageServer"));
628:
629: if (ra == null)
630: throw new ConfigException(
631: L
632: .l("ResourceAdapter is required for ActivationSpecServer"));
633:
634: server = new MessageServer(ejbManager);
635:
636: server.setConfigLocation(getFilename(), getLine());
637:
638: server.setModuleName(getEJBModuleName());
639: server.setEJBName(getEJBName());
640: server.setMappedName(getMappedName());
641: server.setId(getEJBModuleName() + "#" + getMappedName());
642:
643: server.setContainerTransaction(isContainerTransaction());
644:
645: server.setEjbClass(getEJBClass());
646:
647: Class contextImplClass = javaGen
648: .loadClass(getSkeletonName());
649:
650: server.setContextImplClass(contextImplClass);
651:
652: server.setActivationSpec(spec);
653: server.setResourceAdapter(ra);
654:
655: // server.setMessageListenerType(_messagingType);
656:
657: Class beanClass = javaGen
658: .loadClass(getEJBClass().getName());
659:
660: Thread thread = Thread.currentThread();
661: ClassLoader oldLoader = thread.getContextClassLoader();
662:
663: try {
664: thread.setContextClassLoader(server.getClassLoader());
665:
666: ContainerProgram initContainer = getInitProgram();
667:
668: server.setInitProgram(initContainer);
669:
670: if (getServerProgram() != null)
671: getServerProgram().configure(server);
672: } finally {
673: thread.setContextClassLoader(oldLoader);
674: }
675: } catch (Exception e) {
676: throw error(e);
677: }
678:
679: return server;
680: }
681:
682: public class ActivationConfig {
683: public void addActivationConfigProperty(
684: ActivationConfigPropertyConfig prop)
685: throws NamingException {
686: String name = prop.getActivationConfigPropertyName();
687: String value = prop.getActivationConfigPropertyValue();
688:
689: EjbMessageBean.this
690: .addActivationConfigProperty(name, value);
691: }
692: }
693:
694: public static class ActivationConfigPropertyConfig {
695: String _name;
696: String _value;
697:
698: public void setActivationConfigPropertyName(String name) {
699: _name = name;
700: }
701:
702: public String getActivationConfigPropertyName() {
703: return _name;
704: }
705:
706: public void setActivationConfigPropertyValue(String value) {
707: _value = value;
708: }
709:
710: public String getActivationConfigPropertyValue() {
711: return _value;
712: }
713: }
714:
715: public class MessageDrivenDestination {
716: public void setDestinationType(String value)
717: throws ConfigException, NamingException {
718: setMessageDestinationType(value);
719: }
720:
721: public void setSubscriptionDurability(String durability) {
722: }
723:
724: public void setJndiName(JndiBuilder destination)
725: throws ConfigException, NamingException {
726: setDestination((Destination) destination.getObject());
727: }
728: }
729: }
|