001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.inflow;
023:
024: import java.lang.reflect.Method;
025: import java.util.ArrayList;
026: import java.util.Collection;
027: import java.util.HashMap;
028: import java.util.Iterator;
029:
030: import javax.ejb.EJBMetaData;
031: import javax.management.ObjectName;
032: import javax.resource.spi.ActivationSpec;
033: import javax.resource.spi.UnavailableException;
034: import javax.resource.spi.endpoint.MessageEndpoint;
035: import javax.resource.spi.endpoint.MessageEndpointFactory;
036: import javax.transaction.xa.XAResource;
037:
038: import org.jboss.deployment.DeploymentException;
039: import org.jboss.ejb.Container;
040: import org.jboss.ejb.EJBProxyFactory;
041: import org.jboss.ejb.MessageDrivenContainer;
042: import org.jboss.invocation.Invocation;
043: import org.jboss.invocation.InvocationType;
044: import org.jboss.invocation.InvokerInterceptor;
045: import org.jboss.metadata.ActivationConfigPropertyMetaData;
046: import org.jboss.metadata.InvokerProxyBindingMetaData;
047: import org.jboss.metadata.MessageDestinationMetaData;
048: import org.jboss.metadata.MessageDrivenMetaData;
049: import org.jboss.metadata.MetaData;
050: import org.jboss.mx.util.JMXExceptionDecoder;
051: import org.jboss.proxy.GenericProxyFactory;
052: import org.jboss.system.ServiceMBeanSupport;
053: import org.w3c.dom.Element;
054: import org.w3c.dom.Node;
055: import org.w3c.dom.NodeList;
056:
057: import EDU.oswego.cs.dl.util.concurrent.SynchronizedInt;
058:
059: /**
060: * EJBProxyFactory for inflow message driven beans
061: *
062: * @jmx:mbean extends="org.jboss.system.ServiceMBean"
063: *
064: * @author <a href="mailto:adrian@jboss.com">Adrian Brock</a> .
065: * @version <tt>$Revision: 57209 $</tt>
066: */
067: public class JBossMessageEndpointFactory extends ServiceMBeanSupport
068: implements EJBProxyFactory, MessageEndpointFactory,
069: JBossMessageEndpointFactoryMBean {
070: // Constants -----------------------------------------------------
071:
072: // Attributes ----------------------------------------------------
073:
074: /** Whether trace is enabled */
075: protected boolean trace = log.isTraceEnabled();
076:
077: /** Our container */
078: protected MessageDrivenContainer container;
079:
080: /** Our meta data */
081: protected MessageDrivenMetaData metaData;
082:
083: /** The invoker binding */
084: protected String invokerBinding;
085:
086: /** The invoker meta data */
087: protected InvokerProxyBindingMetaData invokerMetaData;
088:
089: /** The activation properties */
090: protected HashMap properties = new HashMap();
091:
092: /** The proxy factory */
093: protected GenericProxyFactory proxyFactory = new GenericProxyFactory();
094:
095: /** The messaging type class */
096: protected Class messagingTypeClass;
097:
098: /** The resource adapter name */
099: protected String resourceAdapterName;
100:
101: /** The resource adapter object name */
102: protected ObjectName resourceAdapterObjectName;
103:
104: /** The activation spec */
105: protected ActivationSpec activationSpec;
106:
107: /** The interceptors */
108: protected ArrayList interceptors;
109:
110: /** The interfaces */
111: protected Class[] interfaces;
112:
113: /** The next proxy id */
114: protected SynchronizedInt nextProxyId = new SynchronizedInt(0);
115:
116: // Static --------------------------------------------------------
117:
118: /** The signature for createActivationSpec */
119: protected String[] createActivationSpecSig = new String[] {
120: Class.class.getName(), Collection.class.getName() };
121:
122: /** The signature for activate/deactivateEndpint */
123: protected String[] activationSig = new String[] {
124: MessageEndpointFactory.class.getName(),
125: ActivationSpec.class.getName() };
126:
127: // Constructors --------------------------------------------------
128:
129: // Public --------------------------------------------------------
130:
131: /**
132: * Get the message driven container
133: *
134: * @return the container
135: */
136: public MessageDrivenContainer getContainer() {
137: return container;
138: }
139:
140: /**
141: * Display the configuration
142: *
143: * @jmx:managed-attribute
144: *
145: * @return the configuration
146: */
147: public String getConfig() {
148: return toString();
149: }
150:
151: // MessageEndpointFactory implementation -------------------------
152:
153: public MessageEndpoint createEndpoint(XAResource resource)
154: throws UnavailableException {
155: trace = log.isTraceEnabled();
156:
157: if (getState() != STARTED && getState() != STARTING)
158: throw new UnavailableException(
159: "The container is not started");
160:
161: HashMap context = new HashMap();
162: context.put(
163: MessageEndpointInterceptor.MESSAGE_ENDPOINT_FACTORY,
164: this );
165: context.put(
166: MessageEndpointInterceptor.MESSAGE_ENDPOINT_XARESOURCE,
167: resource);
168:
169: String ejbName = container.getBeanMetaData()
170: .getContainerObjectNameJndiName();
171:
172: if (trace)
173: log.trace("createEndpoint " + this + " xaResource="
174: + resource);
175:
176: MessageEndpoint endpoint = (MessageEndpoint) proxyFactory
177: .createProxy(ejbName + "@" + nextProxyId.increment(),
178: container.getServiceName(), InvokerInterceptor
179: .getLocal(), null, null, interceptors,
180: container.getClassLoader(), interfaces, context);
181:
182: if (trace)
183: log.trace("Created endpoint " + endpoint + " from " + this );
184:
185: return endpoint;
186: }
187:
188: public boolean isDeliveryTransacted(Method method)
189: throws NoSuchMethodException {
190: boolean result = false;
191: int transType = metaData.getMethodTransactionType(method
192: .getName(), method.getParameterTypes(),
193: InvocationType.LOCAL);
194: if (transType == MetaData.TX_REQUIRED)
195: result = true;
196: if (trace)
197: log.trace("isDeliveryTransacted "
198: + container.getBeanMetaData()
199: .getContainerObjectNameJndiName()
200: + " method=" + method + " result=" + result);
201: return result;
202: }
203:
204: // ServiceMBeanSupport overrides ---------------------------------
205:
206: protected void startService() throws Exception {
207: // Lets take a reference to our metadata
208: metaData = (MessageDrivenMetaData) container.getBeanMetaData();
209: // Resolve the message listener
210: resolveMessageListener();
211: // Resolve the resource adapter
212: resolveResourceAdapter();
213: // Create the activation config
214: createActivationSpec();
215: // Set up proxy parameters
216: setupProxyParameters();
217: // Activate
218: activate();
219: }
220:
221: protected void stopService() throws Exception {
222: // Deactivate
223: deactivate();
224: }
225:
226: // EJBProxyFactory implementation --------------------------------
227:
228: public boolean isIdentical(Container container, Invocation mi) {
229: throw new Error("Not valid for MessageDriven beans");
230: }
231:
232: public Object getEJBHome() {
233: throw new Error("Not valid for MessageDriven beans");
234: }
235:
236: public EJBMetaData getEJBMetaData() {
237: throw new Error("Not valid for MessageDriven beans");
238: }
239:
240: public Collection getEntityCollection(Collection collection) {
241: throw new Error("Not valid for MessageDriven beans");
242: }
243:
244: public Object getEntityEJBObject(Object id) {
245: throw new Error("Not valid for MessageDriven beans");
246: }
247:
248: public Object getStatefulSessionEJBObject(Object id) {
249: throw new Error("Not valid for MessageDriven beans");
250: }
251:
252: public Object getStatelessSessionEJBObject() {
253: throw new Error("Not valid for MessageDriven beans");
254: }
255:
256: public void setInvokerBinding(String binding) {
257: this .invokerBinding = binding;
258: }
259:
260: public void setInvokerMetaData(InvokerProxyBindingMetaData imd) {
261: this .invokerMetaData = imd;
262: }
263:
264: // ContainerService implementation -------------------------------
265:
266: /**
267: * Set the container for which this is an invoker to.
268: *
269: * @param container The container for which this is an invoker to.
270: */
271: public void setContainer(final Container container) {
272: this .container = (MessageDrivenContainer) container;
273: }
274:
275: // Object overrides ----------------------------------------------
276:
277: /**
278: * Return a string representation of the current config state.
279: */
280: public String toString() {
281: StringBuffer buffer = new StringBuffer(100);
282: buffer.append(super .toString());
283: buffer.append("{ resourceAdapter=").append(
284: resourceAdapterObjectName);
285: buffer.append(", messagingType=").append(
286: messagingTypeClass.getName());
287: buffer.append(", ejbName=").append(
288: container.getBeanMetaData()
289: .getContainerObjectNameJndiName());
290: buffer.append(", activationConfig=")
291: .append(properties.values());
292: buffer.append(", activationSpec=").append(activationSpec);
293: buffer.append("}");
294: return buffer.toString();
295: }
296:
297: // Protected -----------------------------------------------------
298:
299: /**
300: * Resolve message listener class
301: *
302: * @throws DeploymentException for any error
303: */
304: protected void resolveMessageListener() throws DeploymentException {
305: String messagingType = metaData.getMessagingType();
306: try {
307: messagingTypeClass = GetTCLAction.getContextClassLoader()
308: .loadClass(messagingType);
309: } catch (Exception e) {
310: DeploymentException.rethrowAsDeploymentException(
311: "Could not load messaging-type class "
312: + messagingType, e);
313: }
314: }
315:
316: /**
317: * Resolve the resource adapter name
318: *
319: * @return the resource adapter name
320: * @throws DeploymentException for any error
321: */
322: protected String resolveResourceAdapterName()
323: throws DeploymentException {
324: return metaData.getResourceAdapterName();
325: }
326:
327: /**
328: * Resolve the resource adapter
329: *
330: * @throws DeploymentException for any error
331: */
332: protected void resolveResourceAdapter() throws DeploymentException {
333: resourceAdapterName = resolveResourceAdapterName();
334: try {
335: resourceAdapterObjectName = new ObjectName(
336: "jboss.jca:service=RARDeployment,name='"
337: + resourceAdapterName + "'");
338: int state = ((Integer) server.getAttribute(
339: resourceAdapterObjectName, "State")).intValue();
340: if (state != STARTED)
341: throw new DeploymentException(
342: "The resource adapter is not started "
343: + resourceAdapterName);
344: } catch (Exception e) {
345: DeploymentException.rethrowAsDeploymentException(
346: "Cannot locate resource adapter deployment "
347: + resourceAdapterName, e);
348: }
349: }
350:
351: /**
352: * Set up the proxy parametrs
353: *
354: * @throws DeploymentException
355: */
356: protected void setupProxyParameters() throws DeploymentException {
357: // Set the interfaces
358: interfaces = new Class[] { MessageEndpoint.class,
359: messagingTypeClass };
360:
361: // Set the interceptors
362: interceptors = new ArrayList();
363: Element proxyConfig = invokerMetaData.getProxyFactoryConfig();
364: Element endpointInterceptors = MetaData.getOptionalChild(
365: proxyConfig, "endpoint-interceptors", null);
366: if (endpointInterceptors == null)
367: throw new DeploymentException(
368: "No endpoint interceptors found");
369: else {
370: NodeList children = endpointInterceptors
371: .getElementsByTagName("interceptor");
372: for (int i = 0; i < children.getLength(); ++i) {
373: Node currentChild = children.item(i);
374: if (currentChild.getNodeType() == Node.ELEMENT_NODE) {
375: Element interceptor = (Element) children.item(i);
376: String className = MetaData
377: .getElementContent(interceptor);
378: try {
379: Class clazz = container.getClassLoader()
380: .loadClass(className);
381: interceptors.add(clazz);
382: } catch (Throwable t) {
383: DeploymentException
384: .rethrowAsDeploymentException(
385: "Error loading interceptor class "
386: + className, t);
387: }
388: }
389: }
390: }
391: }
392:
393: /**
394: * Add activation config properties
395: *
396: * @throws DeploymentException for any error
397: */
398: protected void augmentActivationConfigProperties()
399: throws DeploymentException {
400: // Allow activation config properties from invoker proxy binding
401: Element proxyConfig = invokerMetaData.getProxyFactoryConfig();
402: Element activationConfig = MetaData.getOptionalChild(
403: proxyConfig, "activation-config");
404: if (activationConfig != null) {
405: Iterator iterator = MetaData.getChildrenByTagName(
406: activationConfig, "activation-config-property");
407: while (iterator.hasNext()) {
408: Element resourceRef = (Element) iterator.next();
409: ActivationConfigPropertyMetaData metaData = new ActivationConfigPropertyMetaData();
410: metaData.importXml(resourceRef);
411: if (properties.containsKey(metaData.getName()) == false)
412: properties.put(metaData.getName(), metaData);
413: }
414: }
415:
416: // Message Destination Link
417: String link = metaData.getDestinationLink();
418: if (link != null) {
419: link = link.trim();
420: if (link.length() > 0) {
421: if (properties.containsKey("destination"))
422: log.warn("Ignoring message-destination-link '"
423: + link + "' when the destination "
424: + "is already in the activation-config.");
425: else {
426: MessageDestinationMetaData destinationMetaData = container
427: .getMessageDestination(link);
428: if (destinationMetaData == null)
429: throw new DeploymentException(
430: "Unresolved message-destination-link '"
431: + link
432: + "' no message-destination in ejb-jar.xml");
433: String jndiName = destinationMetaData.getJNDIName();
434: if (jndiName == null)
435: throw new DeploymentException(
436: "The message-destination '"
437: + link
438: + "' has no jndi-name in jboss.xml");
439: properties.put("destination", jndiName);
440: }
441: }
442: }
443: }
444:
445: /**
446: * Create the activation spec
447: *
448: * @throws DeploymentException for any error
449: */
450: protected void createActivationSpec() throws DeploymentException {
451: properties = new HashMap(metaData
452: .getActivationConfigProperties());
453: augmentActivationConfigProperties();
454:
455: Object[] params = new Object[] { messagingTypeClass,
456: properties.values() };
457: try {
458: activationSpec = (ActivationSpec) server.invoke(
459: resourceAdapterObjectName, "createActivationSpec",
460: params, createActivationSpecSig);
461: } catch (Throwable t) {
462: t = JMXExceptionDecoder.decode(t);
463: DeploymentException.rethrowAsDeploymentException(
464: "Unable to create activation spec ra="
465: + resourceAdapterObjectName
466: + " messaging-type="
467: + messagingTypeClass.getName()
468: + " properties="
469: + metaData.getActivationConfigProperties(),
470: t);
471: }
472: }
473:
474: /**
475: * Activate
476: *
477: * @throws DeploymentException for any error
478: */
479: protected void activate() throws DeploymentException {
480: Object[] params = new Object[] { this , activationSpec };
481: try {
482: server.invoke(resourceAdapterObjectName,
483: "endpointActivation", params, activationSig);
484: } catch (Throwable t) {
485: t = JMXExceptionDecoder.decode(t);
486: DeploymentException.rethrowAsDeploymentException(
487: "Endpoint activation failed ra="
488: + resourceAdapterObjectName
489: + " activationSpec=" + activationSpec, t);
490: }
491: }
492:
493: /**
494: * Deactivate
495: */
496: protected void deactivate() {
497: Object[] params = new Object[] { this , activationSpec };
498: try {
499: server.invoke(resourceAdapterObjectName,
500: "endpointDeactivation", params, activationSig);
501: } catch (Throwable t) {
502: t = JMXExceptionDecoder.decode(t);
503: log.warn("Endpoint activation failed ra="
504: + resourceAdapterObjectName + " activationSpec="
505: + activationSpec, t);
506: }
507: }
508:
509: // Package Private -----------------------------------------------
510:
511: // Private -------------------------------------------------------
512:
513: // Inner Classes -------------------------------------------------
514: }
|