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.metadata;
023:
024: import java.util.Collection;
025: import java.util.HashMap;
026: import java.util.Iterator;
027:
028: import javax.jms.Message;
029: import javax.jms.Session;
030:
031: import org.w3c.dom.Element;
032: import org.w3c.dom.NodeList;
033:
034: import org.jboss.invocation.InvocationType;
035: import org.jboss.deployment.DeploymentException;
036:
037: /**
038: * Provides a container and parser for the metadata of a message driven bean.
039: *
040: * <p>Have to add changes ApplicationMetaData and ConfigurationMetaData.
041: *
042: * @author <a href="mailto:sebastien.alborini@m4x.org">Sebastien Alborini</a>
043: * @author <a href="mailto:peter.antman@tim.se">Peter Antman</a>
044: * @author <a href="mailto:andreas@jboss.org">Andreas Schaefer</a>
045: * @author <a href="mailto:adrian@jboss.com">Adrian Brock</a>
046: * @version $Revision: 57209 $
047: */
048: public class MessageDrivenMetaData extends BeanMetaData {
049: // Constants -----------------------------------------------------
050:
051: public static final int AUTO_ACKNOWLEDGE_MODE = Session.AUTO_ACKNOWLEDGE;
052: public static final int DUPS_OK_ACKNOWLEDGE_MODE = Session.DUPS_OK_ACKNOWLEDGE;
053: public static final int CLIENT_ACKNOWLEDGE_MODE = Session.CLIENT_ACKNOWLEDGE;
054: public static final byte DURABLE_SUBSCRIPTION = 0;
055: public static final byte NON_DURABLE_SUBSCRIPTION = 1;
056: public static final byte TX_UNSET = 9;
057: public static final String DEFAULT_MESSAGE_DRIVEN_BEAN_INVOKER_PROXY_BINDING = "message-driven-bean";
058:
059: public static final String DEFAULT_MESSAGING_TYPE = "javax.jms.MessageListener";
060:
061: // Attributes ----------------------------------------------------
062:
063: private int acknowledgeMode = AUTO_ACKNOWLEDGE_MODE;
064: private byte subscriptionDurability = NON_DURABLE_SUBSCRIPTION;
065: private byte methodTransactionType = TX_UNSET;
066: private String messagingType;
067: private String destinationType;
068: private String destinationLink;
069: private String messageSelector;
070: private String destinationJndiName;
071: private String user;
072: private String passwd;
073: private String clientId;
074: private String subscriptionId;
075: /** The activation config properties */
076: private HashMap activationConfigProperties = new HashMap();
077: /** The resource adapter name */
078: private String resourceAdapterName;
079:
080: // Static --------------------------------------------------------
081:
082: // Constructors --------------------------------------------------
083:
084: public MessageDrivenMetaData(ApplicationMetaData app) {
085: super (app, BeanMetaData.MDB_TYPE);
086: }
087:
088: // Public --------------------------------------------------------
089:
090: /**
091: * Get the message acknowledgement mode.
092: *
093: * @return MessageDrivenMetaData.AUTO_ACKNOWLADGE_MODE or
094: * MessageDrivenMetaData.DUPS_OK_AKNOWLEDGE_MODE or
095: * MessageDrivenMetaData.CLIENT_ACKNOWLEDGE_MODE
096: */
097: public int getAcknowledgeMode() {
098: // My interpretation of the EJB and JMS spec leads
099: // me to that CLIENT_ACK is the only possible
100: // solution. A transaction is per session in JMS, and
101: // it is not possible to get access to the transaction.
102: // According to the JMS spec it is possible to
103: // multithread handling of messages (but not session),
104: // but there is NO transaction support for this.
105: // I,e, we can not use the JMS transaction for
106: // message ack: hence we must use manual ack.
107:
108: // But for NOT_SUPPORTED this is not true here we
109: // should have AUTO_ACKNOWLEDGE_MODE
110:
111: // This is not true for now. For JBossMQ we relly
112: // completely on transaction handling. For JBossMQ, the
113: // ackmode is actually not relevant. We keep it here
114: // anyway, if we find that this is needed for other
115: // JMS provider, or is not good.
116:
117: if (getMethodTransactionType() == TX_REQUIRED) {
118: return CLIENT_ACKNOWLEDGE_MODE;
119: } else {
120: return acknowledgeMode;
121: }
122: }
123:
124: public String getMessagingType() {
125: return messagingType;
126: }
127:
128: public boolean isJMSMessagingType() {
129: return DEFAULT_MESSAGING_TYPE.equals(messagingType);
130: }
131:
132: public String getDestinationType() {
133: return destinationType;
134: }
135:
136: public String getDestinationLink() {
137: return destinationLink;
138: }
139:
140: public String getMessageSelector() {
141: return messageSelector;
142: }
143:
144: public String getDestinationJndiName() {
145: return destinationJndiName;
146: }
147:
148: public String getUser() {
149: return user;
150: }
151:
152: public String getPasswd() {
153: return passwd;
154: }
155:
156: public String getClientId() {
157: return clientId;
158: }
159:
160: public String getSubscriptionId() {
161: return subscriptionId;
162: }
163:
164: /**
165: * Check MDB methods TX type, is cached here
166: */
167: public byte getMethodTransactionType() {
168: if (methodTransactionType == TX_UNSET) {
169: Class[] sig = { Message.class };
170: methodTransactionType = getMethodTransactionType(
171: "onMessage", sig);
172: }
173: return methodTransactionType;
174: }
175:
176: /**
177: * Check MDB methods TX type, is cached here
178: */
179: public byte getMethodTransactionType(String methodName,
180: Class[] signature) {
181: if (isContainerManagedTx()) {
182: if (super .getMethodTransactionType(methodName, signature,
183: null) == MetaData.TX_NOT_SUPPORTED)
184: return TX_NOT_SUPPORTED;
185: else
186: return TX_REQUIRED;
187: } else
188: return TX_UNKNOWN;
189: }
190:
191: /**
192: * Overide here, since a message driven bean only ever have one method,
193: * which we might cache.
194: */
195: public byte getMethodTransactionType(String methodName,
196: Class[] params, InvocationType iface) {
197: // A JMS MDB may only ever have one method
198: if (isJMSMessagingType())
199: return getMethodTransactionType();
200: else
201: return getMethodTransactionType(methodName, params);
202: }
203:
204: /**
205: * Get the subscription durability mode.
206: *
207: * @return MessageDrivenMetaData.DURABLE_SUBSCRIPTION or
208: * MessageDrivenMetaData.NON_DURABLE_SUBSCRIPTION
209: */
210: public byte getSubscriptionDurability() {
211: return subscriptionDurability;
212: }
213:
214: public String getDefaultConfigurationName() {
215: if (isJMSMessagingType() == false)
216: return ConfigurationMetaData.MESSAGE_INFLOW_DRIVEN;
217: else
218: return ConfigurationMetaData.MESSAGE_DRIVEN_13;
219: }
220:
221: /**
222: * Get all the activation config properties
223: *
224: * @return a collection of ActivationConfigPropertyMetaData elements
225: */
226: public HashMap getActivationConfigProperties() {
227: return activationConfigProperties;
228: }
229:
230: /**
231: * Get a particular activation config property
232: *
233: * @param name the name of the property
234: * @return the ActivationConfigPropertyMetaData or null if not found
235: */
236: public ActivationConfigPropertyMetaData getActivationConfigProperty(
237: String name) {
238: return (ActivationConfigPropertyMetaData) activationConfigProperties
239: .get(name);
240: }
241:
242: /**
243: * Get the resource adapter name
244: *
245: * @return the resource adapter name or null if none specified
246: */
247: public String getResourceAdapterName() {
248: return resourceAdapterName;
249: }
250:
251: public void importEjbJarXml(Element element)
252: throws DeploymentException {
253: super .importEjbJarXml(element);
254:
255: messageSelector = getOptionalChildContent(element,
256: "message-selector");
257: if (messageSelector != null) {
258: //AS Check for Carriage Returns, remove them and trim the selector
259: int i = -1;
260: // Note this only works this way because the search and replace are distinct
261: while ((i = messageSelector.indexOf("\r\n")) >= 0) {
262: // Replace \r\n by a space
263: messageSelector = (i == 0 ? "" : messageSelector
264: .substring(0, i))
265: + " "
266: + (i >= messageSelector.length() - 2 ? ""
267: : messageSelector.substring(i + 2));
268: }
269: i = -1;
270: while ((i = messageSelector.indexOf("\r")) >= 0) {
271: // Replace \r by a space
272: messageSelector = (i == 0 ? "" : messageSelector
273: .substring(0, i))
274: + " "
275: + (i >= messageSelector.length() - 1 ? ""
276: : messageSelector.substring(i + 1));
277: }
278: i = -1;
279: while ((i = messageSelector.indexOf("\n")) >= 0) {
280: // Replace \n by a space
281: messageSelector = (i == 0 ? "" : messageSelector
282: .substring(0, i))
283: + " "
284: + (i >= messageSelector.length() - 1 ? ""
285: : messageSelector.substring(i + 1));
286: }
287: // Finally trim it. This is here because only carriage returns and linefeeds are transformed
288: // to spaces
289: messageSelector = messageSelector.trim();
290: if ("".equals(messageSelector)) {
291: messageSelector = null;
292: }
293: }
294:
295: // messaging-type is new to EJB-2.1
296: messagingType = getOptionalChildContent(element,
297: "messaging-type", DEFAULT_MESSAGING_TYPE);
298:
299: // destination is optional
300: Element destination = getOptionalChild(element,
301: "message-driven-destination");
302: if (destination != null) {
303: destinationType = getUniqueChildContent(destination,
304: "destination-type");
305: if (destinationType.equals("javax.jms.Topic")) {
306: String subscr = getOptionalChildContent(destination,
307: "subscription-durability");
308: // Should we do sanity check??
309: if (subscr != null && subscr.equals("Durable")) {
310: subscriptionDurability = DURABLE_SUBSCRIPTION;
311: } else {
312: subscriptionDurability = NON_DURABLE_SUBSCRIPTION;//Default
313: }
314: }
315: }
316:
317: // look for an EJB21 destination type
318: String ejb21DestinationType = getOptionalChildContent(element,
319: "message-destination-type");
320: if (ejb21DestinationType != null)
321: destinationType = ejb21DestinationType;
322:
323: // destination link is optional
324: destinationLink = getOptionalChildContent(element,
325: "message-destination-link");
326:
327: // set the transaction type
328: String transactionType = getUniqueChildContent(element,
329: "transaction-type");
330: if (transactionType.equals("Bean")) {
331: containerManagedTx = false;
332: String ack = getOptionalChildContent(element,
333: "acknowledge-mode");
334: if (ack == null || ack.equalsIgnoreCase("Auto-acknowledge")
335: || ack.equalsIgnoreCase("AUTO_ACKNOWLEDGE")) {
336: acknowledgeMode = AUTO_ACKNOWLEDGE_MODE;
337: } else if (ack.equalsIgnoreCase("Dups-ok-acknowledge")
338: || ack.equalsIgnoreCase("DUPS_OK_ACKNOWLEDGE")) {
339: acknowledgeMode = DUPS_OK_ACKNOWLEDGE_MODE;
340: } else {
341: throw new DeploymentException(
342: "invalid acknowledge-mode: " + ack);
343: }
344: } else if (transactionType.equals("Container")) {
345: containerManagedTx = true;
346: } else {
347: throw new DeploymentException(
348: "transaction type should be 'Bean' or 'Container'");
349: }
350:
351: // Set the activation config properties
352: Element activationConfig = getOptionalChild(element,
353: "activation-config");
354: if (activationConfig != null) {
355: Iterator iterator = getChildrenByTagName(activationConfig,
356: "activation-config-property");
357: while (iterator.hasNext()) {
358: Element resourceRef = (Element) iterator.next();
359: ActivationConfigPropertyMetaData metaData = new ActivationConfigPropertyMetaData();
360: metaData.importXml(resourceRef);
361: if (activationConfigProperties.containsKey(metaData
362: .getName()))
363: throw new DeploymentException(
364: "Duplicate activation-config-property-name: "
365: + metaData.getName());
366: activationConfigProperties.put(metaData.getName(),
367: metaData);
368: }
369: }
370: }
371:
372: public void importJbossXml(Element element)
373: throws DeploymentException {
374: super .importJbossXml(element);
375:
376: // set the jndi name, (optional)
377: destinationJndiName = getOptionalChildContent(element,
378: "destination-jndi-name");
379: user = getOptionalChildContent(element, "mdb-user");
380: passwd = getOptionalChildContent(element, "mdb-passwd");
381: clientId = getOptionalChildContent(element, "mdb-client-id");
382: subscriptionId = getOptionalChildContent(element,
383: "mdb-subscription-id");
384: resourceAdapterName = getOptionalChildContent(element,
385: "resource-adapter-name");
386:
387: // Allow activation config properties to be overriden in jboss.xml
388: Element activationConfig = getOptionalChild(element,
389: "activation-config");
390: if (activationConfig != null) {
391: Iterator iterator = getChildrenByTagName(activationConfig,
392: "activation-config-property");
393: while (iterator.hasNext()) {
394: Element resourceRef = (Element) iterator.next();
395: ActivationConfigPropertyMetaData metaData = new ActivationConfigPropertyMetaData();
396: metaData.importXml(resourceRef);
397: activationConfigProperties.put(metaData.getName(),
398: metaData);
399: }
400: }
401: }
402:
403: public void defaultInvokerBindings() {
404: this .invokerBindings = new HashMap();
405: this .invokerBindings.put(
406: DEFAULT_MESSAGE_DRIVEN_BEAN_INVOKER_PROXY_BINDING,
407: getJndiName());
408: }
409:
410: // Package protected ---------------------------------------------
411:
412: // Protected -----------------------------------------------------
413:
414: // Private -------------------------------------------------------
415:
416: // Inner classes -------------------------------------------------
417: }
|