001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.activemq.ra;
018
019 import java.beans.IntrospectionException;
020 import java.beans.PropertyDescriptor;
021 import java.io.Serializable;
022 import java.util.ArrayList;
023 import java.util.Iterator;
024 import java.util.List;
025
026 import javax.jms.Queue;
027 import javax.jms.Session;
028 import javax.jms.Topic;
029 import javax.resource.ResourceException;
030 import javax.resource.spi.InvalidPropertyException;
031 import javax.resource.spi.ResourceAdapter;
032
033 import org.apache.activemq.RedeliveryPolicy;
034 import org.apache.activemq.command.ActiveMQDestination;
035 import org.apache.activemq.command.ActiveMQQueue;
036 import org.apache.activemq.command.ActiveMQTopic;
037 import org.apache.activemq.selector.SelectorParser;
038
039 /**
040 * Configures the inbound JMS consumer specification using ActiveMQ
041 *
042 * @org.apache.xbean.XBean element="activationSpec"
043 * @version $Revision: 812941 $ $Date: 2007-08-11 17:29:21 -0400 (Sat, 11 Aug
044 * 2007) $
045 */
046 public class ActiveMQActivationSpec implements MessageActivationSpec, Serializable {
047
048 /** Auto-acknowledge constant for <code>acknowledgeMode</code> property * */
049 public static final String AUTO_ACKNOWLEDGE_MODE = "Auto-acknowledge";
050 /** Dups-ok-acknowledge constant for <code>acknowledgeMode</code> property * */
051 public static final String DUPS_OK_ACKNOWLEDGE_MODE = "Dups-ok-acknowledge";
052 /** Durable constant for <code>subscriptionDurability</code> property * */
053 public static final String DURABLE_SUBSCRIPTION = "Durable";
054 /** NonDurable constant for <code>subscriptionDurability</code> property * */
055 public static final String NON_DURABLE_SUBSCRIPTION = "NonDurable";
056 public static final int INVALID_ACKNOWLEDGE_MODE = -1;
057
058 private static final long serialVersionUID = -7153087544100459975L;
059
060 private transient MessageResourceAdapter resourceAdapter;
061 private String destinationType;
062 private String messageSelector;
063 private String destination;
064 private String acknowledgeMode = AUTO_ACKNOWLEDGE_MODE;
065 private String userName;
066 private String password;
067 private String clientId;
068 private String subscriptionName;
069 private String subscriptionDurability = NON_DURABLE_SUBSCRIPTION;
070 private String noLocal = "false";
071 private String useRAManagedTransaction = "false";
072 private String maxSessions = "10";
073 private String maxMessagesPerSessions = "10";
074 private String enableBatch = "false";
075 private String maxMessagesPerBatch = "10";
076 private RedeliveryPolicy redeliveryPolicy;
077
078 /**
079 * @see javax.resource.spi.ActivationSpec#validate()
080 */
081 public void validate() throws InvalidPropertyException {
082 List<String> errorMessages = new ArrayList<String>();
083 List<PropertyDescriptor> propsNotSet = new ArrayList<PropertyDescriptor>();
084 try {
085 if (!isValidDestination(errorMessages)) {
086 propsNotSet.add(new PropertyDescriptor("destination", ActiveMQActivationSpec.class));
087 }
088 if (!isValidDestinationType(errorMessages)) {
089 propsNotSet.add(new PropertyDescriptor("destinationType", ActiveMQActivationSpec.class));
090 }
091 if (!isValidAcknowledgeMode(errorMessages)) {
092 propsNotSet.add(new PropertyDescriptor("acknowledgeMode", ActiveMQActivationSpec.class));
093 }
094 if (!isValidSubscriptionDurability(errorMessages)) {
095 propsNotSet.add(new PropertyDescriptor("subscriptionDurability", ActiveMQActivationSpec.class));
096 }
097 if (!isValidClientId(errorMessages)) {
098 propsNotSet.add(new PropertyDescriptor("clientId", ActiveMQActivationSpec.class));
099 }
100 if (!isValidSubscriptionName(errorMessages)) {
101 propsNotSet.add(new PropertyDescriptor("subscriptionName", ActiveMQActivationSpec.class));
102 }
103 if (!isValidMaxMessagesPerSessions(errorMessages)) {
104 propsNotSet.add(new PropertyDescriptor("maxMessagesPerSessions", ActiveMQActivationSpec.class));
105 }
106 if (!isValidMaxSessions(errorMessages)) {
107 propsNotSet.add(new PropertyDescriptor("maxSessions", ActiveMQActivationSpec.class));
108 }
109 if (!isValidMessageSelector(errorMessages)) {
110 propsNotSet.add(new PropertyDescriptor("messageSelector", ActiveMQActivationSpec.class));
111 }
112 if (!isValidNoLocal(errorMessages)) {
113 propsNotSet.add(new PropertyDescriptor("noLocal", ActiveMQActivationSpec.class));
114 }
115 if (!isValidUseRAManagedTransaction(errorMessages)) {
116 propsNotSet.add(new PropertyDescriptor("useRAManagedTransaction", ActiveMQActivationSpec.class));
117 }
118 if (!isValidEnableBatch(errorMessages)) {
119 propsNotSet.add(new PropertyDescriptor("enableBatch", ActiveMQActivationSpec.class));
120 }
121 if (!isValidMaxMessagesPerBatch(errorMessages)) {
122 propsNotSet.add(new PropertyDescriptor("maxMessagesPerBatch", ActiveMQActivationSpec.class));
123 }
124
125 } catch (IntrospectionException e) {
126 e.printStackTrace();
127 }
128
129 if (propsNotSet.size() > 0) {
130 StringBuffer b = new StringBuffer();
131 b.append("Invalid settings:");
132 for (Iterator<String> iter = errorMessages.iterator(); iter.hasNext();) {
133 b.append(" ");
134 b.append(iter.next());
135 }
136 InvalidPropertyException e = new InvalidPropertyException(b.toString());
137 final PropertyDescriptor[] descriptors = propsNotSet.toArray(new PropertyDescriptor[propsNotSet.size()]);
138 e.setInvalidPropertyDescriptors(descriptors);
139 throw e;
140 }
141 }
142
143 public boolean isValidUseRAManagedTransaction(List<String> errorMessages) {
144 try {
145 new Boolean(noLocal);
146 return true;
147 } catch (Throwable e) {
148 //
149 }
150 errorMessages.add("noLocal must be set to: true or false.");
151 return false;
152 }
153
154 public boolean isValidNoLocal(List<String> errorMessages) {
155 try {
156 new Boolean(noLocal);
157 return true;
158 } catch (Throwable e) {
159 //
160 }
161 errorMessages.add("noLocal must be set to: true or false.");
162 return false;
163 }
164
165 public boolean isValidMessageSelector(List<String> errorMessages) {
166 try {
167 if (!isEmpty(messageSelector)) {
168 SelectorParser.parse(messageSelector);
169 }
170 return true;
171 } catch (Throwable e) {
172 errorMessages.add("messageSelector not set to valid message selector: " + e.getMessage());
173 return false;
174 }
175 }
176
177 public boolean isValidMaxSessions(List<String> errorMessages) {
178 try {
179 if (Integer.parseInt(maxSessions) > 0) {
180 return true;
181 }
182 } catch (NumberFormatException e) {
183 //
184 }
185 errorMessages.add("maxSessions must be set to number > 0");
186 return false;
187 }
188
189 public boolean isValidMaxMessagesPerSessions(List<String> errorMessages) {
190 try {
191 if (Integer.parseInt(maxMessagesPerSessions) > 0) {
192 return true;
193 }
194 } catch (NumberFormatException e) {
195 //
196 }
197 errorMessages.add("maxMessagesPerSessions must be set to number > 0");
198 return false;
199 }
200
201 public boolean isValidMaxMessagesPerBatch(List<String> errorMessages) {
202 try {
203 if (Integer.parseInt(maxMessagesPerBatch) > 0) {
204 return true;
205 }
206 } catch (NumberFormatException e) {
207 //
208 }
209 errorMessages.add("maxMessagesPerBatch must be set to number > 0");
210 return false;
211 }
212
213 public boolean isValidEnableBatch(List<String> errorMessages) {
214 try {
215 new Boolean(enableBatch);
216 return true;
217 } catch (Throwable e) {
218 //
219 }
220 errorMessages.add("enableBatch must be set to: true or false");
221 return false;
222 }
223
224 public ResourceAdapter getResourceAdapter() {
225 return resourceAdapter;
226 }
227
228 /**
229 * @see javax.resource.spi.ResourceAdapterAssociation#setResourceAdapter(javax.resource.spi.ResourceAdapter)
230 */
231 public void setResourceAdapter(ResourceAdapter resourceAdapter) throws ResourceException {
232 // spec section 5.3.3
233 if (this.resourceAdapter != null) {
234 throw new ResourceException("ResourceAdapter already set");
235 }
236 if (!(resourceAdapter instanceof MessageResourceAdapter)) {
237 throw new ResourceException("ResourceAdapter is not of type: " + MessageResourceAdapter.class.getName());
238 }
239 this.resourceAdapter = (MessageResourceAdapter)resourceAdapter;
240 }
241
242 // ///////////////////////////////////////////////////////////////////////
243 //
244 // Java Bean getters and setters for this ActivationSpec class.
245 //
246 // ///////////////////////////////////////////////////////////////////////
247 public String getDestinationType() {
248 if (!isEmpty(destinationType)) {
249 return destinationType;
250 }
251 return null;
252 }
253
254 /**
255 * @param destinationType The destinationType to set.
256 */
257 public void setDestinationType(String destinationType) {
258 this.destinationType = destinationType;
259 }
260
261 public String getPassword() {
262 if (!isEmpty(password)) {
263 return password;
264 }
265 return null;
266 }
267
268 /**
269 *
270 */
271 public void setPassword(String password) {
272 this.password = password;
273 }
274
275 public String getUserName() {
276 if (!isEmpty(userName)) {
277 return userName;
278 }
279 return null;
280 }
281
282 /**
283 *
284 */
285 public void setUserName(String userName) {
286 this.userName = userName;
287 }
288
289 public String getMessageSelector() {
290 if (!isEmpty(messageSelector)) {
291 return messageSelector;
292 }
293 return null;
294 }
295
296 /**
297 * @param messageSelector The messageSelector to set.
298 */
299 public void setMessageSelector(String messageSelector) {
300 this.messageSelector = messageSelector;
301 }
302
303 public String getNoLocal() {
304 return noLocal;
305 }
306
307 /**
308 * @param noLocal The noLocal to set.
309 */
310 public void setNoLocal(String noLocal) {
311 if (noLocal != null) {
312 this.noLocal = noLocal;
313 }
314 }
315
316 public String getAcknowledgeMode() {
317 if (!isEmpty(acknowledgeMode)) {
318 return acknowledgeMode;
319 }
320 return null;
321 }
322
323 /**
324 *
325 */
326 public void setAcknowledgeMode(String acknowledgeMode) {
327 this.acknowledgeMode = acknowledgeMode;
328 }
329
330 public String getClientId() {
331 if (!isEmpty(clientId)) {
332 return clientId;
333 }
334 return null;
335 }
336
337 /**
338 *
339 */
340 public void setClientId(String clientId) {
341 this.clientId = clientId;
342 }
343
344 public String getDestination() {
345 if (!isEmpty(destination)) {
346 return destination;
347 }
348 return null;
349 }
350
351 /**
352 *
353 */
354 public void setDestination(String destination) {
355 this.destination = destination;
356 }
357
358 public String getSubscriptionDurability() {
359 if (!isEmpty(subscriptionDurability)) {
360 return subscriptionDurability;
361 }
362 return null;
363 }
364
365 /**
366 *
367 */
368 public void setSubscriptionDurability(String subscriptionDurability) {
369 this.subscriptionDurability = subscriptionDurability;
370 }
371
372 public String getSubscriptionName() {
373 if (!isEmpty(subscriptionName)) {
374 return subscriptionName;
375 }
376 return null;
377 }
378
379 /**
380 *
381 */
382 public void setSubscriptionName(String subscriptionName) {
383 this.subscriptionName = subscriptionName;
384 }
385
386 public boolean isValidSubscriptionName(List<String> errorMessages) {
387 if (!isDurableSubscription() ? true : subscriptionName != null && subscriptionName.trim().length() > 0) {
388 return true;
389 }
390 errorMessages.add("subscriptionName must be set since durable subscription was requested.");
391 return false;
392 }
393
394 public boolean isValidClientId(List<String> errorMessages) {
395 if (!isDurableSubscription() ? true : clientId != null && clientId.trim().length() > 0) {
396 return true;
397 }
398 errorMessages.add("clientId must be set since durable subscription was requested.");
399 return false;
400 }
401
402 public boolean isDurableSubscription() {
403 return DURABLE_SUBSCRIPTION.equals(subscriptionDurability);
404 }
405
406 public boolean isValidSubscriptionDurability(List<String> errorMessages) {
407 // subscriptionDurability only applies to Topics
408 if (DURABLE_SUBSCRIPTION.equals(subscriptionDurability) && getDestinationType() != null && !Topic.class.getName().equals(getDestinationType())) {
409 errorMessages.add("subscriptionDurability cannot be set to: " + DURABLE_SUBSCRIPTION + " when destinationType is set to " + Queue.class.getName()
410 + " as it is only valid when destinationType is set to " + Topic.class.getName() + ".");
411 return false;
412 }
413 if (NON_DURABLE_SUBSCRIPTION.equals(subscriptionDurability) || DURABLE_SUBSCRIPTION.equals(subscriptionDurability)) {
414 return true;
415 }
416 errorMessages.add("subscriptionDurability must be set to: " + NON_DURABLE_SUBSCRIPTION + " or " + DURABLE_SUBSCRIPTION + ".");
417 return false;
418 }
419
420 public boolean isValidAcknowledgeMode(List<String> errorMessages) {
421 if (AUTO_ACKNOWLEDGE_MODE.equals(acknowledgeMode) || DUPS_OK_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) {
422 return true;
423 }
424 errorMessages.add("acknowledgeMode must be set to: " + AUTO_ACKNOWLEDGE_MODE + " or " + DUPS_OK_ACKNOWLEDGE_MODE + ".");
425 return false;
426 }
427
428 public boolean isValidDestinationType(List<String> errorMessages) {
429 if (Queue.class.getName().equals(destinationType) || Topic.class.getName().equals(destinationType)) {
430 return true;
431 }
432 errorMessages.add("destinationType must be set to: " + Queue.class.getName() + " or " + Topic.class.getName() + ".");
433 return false;
434 }
435
436 public boolean isValidDestination(List<String> errorMessages) {
437 if (!(destination == null || destination.equals(""))) {
438 return true;
439 }
440 errorMessages.add("destination is a required field and must be set to the destination name.");
441 return false;
442 }
443
444 public boolean isEmpty(String value) {
445 return value == null || "".equals(value.trim());
446 }
447
448 /**
449 *
450 */
451 @Override
452 public String toString() {
453 return "ActiveMQActivationSpec{" + "acknowledgeMode='" + acknowledgeMode + "'" + ", destinationType='" + destinationType + "'" + ", messageSelector='" + messageSelector + "'"
454 + ", destination='" + destination + "'" + ", clientId='" + clientId + "'" + ", subscriptionName='" + subscriptionName + "'" + ", subscriptionDurability='" + subscriptionDurability
455 + "'" + "}";
456 }
457
458 public int getAcknowledgeModeForSession() {
459 if (AUTO_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) {
460 return Session.AUTO_ACKNOWLEDGE;
461 } else if (DUPS_OK_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) {
462 return Session.DUPS_OK_ACKNOWLEDGE;
463 } else {
464 return INVALID_ACKNOWLEDGE_MODE;
465 }
466 }
467
468 /**
469 * A helper method mostly for use in Dependency Injection containers which
470 * allows you to customize the destination and destinationType properties
471 * from a single ActiveMQDestination POJO
472 */
473 public void setActiveMQDestination(ActiveMQDestination destination) {
474 setDestination(destination.getPhysicalName());
475 if (destination instanceof Queue) {
476 setDestinationType(Queue.class.getName());
477 } else {
478 setDestinationType(Topic.class.getName());
479 }
480 }
481
482 /**
483 *
484 */
485 public ActiveMQDestination createDestination() {
486 if (isEmpty(destinationType) || isEmpty(destination)) {
487 return null;
488 }
489
490 ActiveMQDestination dest = null;
491 if (Queue.class.getName().equals(destinationType)) {
492 dest = new ActiveMQQueue(destination);
493 } else if (Topic.class.getName().equals(destinationType)) {
494 dest = new ActiveMQTopic(destination);
495 } else {
496 assert false : "Execution should never reach here";
497 }
498 return dest;
499 }
500
501 public String getMaxMessagesPerSessions() {
502 return maxMessagesPerSessions;
503 }
504
505 /**
506 *
507 */
508 public void setMaxMessagesPerSessions(String maxMessagesPerSessions) {
509 if (maxMessagesPerSessions != null) {
510 this.maxMessagesPerSessions = maxMessagesPerSessions;
511 }
512 }
513
514 public String getMaxSessions() {
515 return maxSessions;
516 }
517
518 /**
519 *
520 */
521 public void setMaxSessions(String maxSessions) {
522 if (maxSessions != null) {
523 this.maxSessions = maxSessions;
524 }
525 }
526
527 public String getUseRAManagedTransaction() {
528 return useRAManagedTransaction;
529 }
530
531 /**
532 *
533 */
534 public void setUseRAManagedTransaction(String useRAManagedTransaction) {
535 if (useRAManagedTransaction != null) {
536 this.useRAManagedTransaction = useRAManagedTransaction;
537 }
538 }
539
540 public int getMaxMessagesPerSessionsIntValue() {
541 return Integer.parseInt(maxMessagesPerSessions);
542 }
543
544 public int getMaxSessionsIntValue() {
545 return Integer.parseInt(maxSessions);
546 }
547
548 public boolean isUseRAManagedTransactionEnabled() {
549 return Boolean.valueOf(useRAManagedTransaction).booleanValue();
550 }
551
552 public boolean getNoLocalBooleanValue() {
553 return Boolean.valueOf(noLocal).booleanValue();
554 }
555
556 public String getEnableBatch() {
557 return enableBatch;
558 }
559
560 /**
561 *
562 */
563 public void setEnableBatch(String enableBatch) {
564 if (enableBatch != null) {
565 this.enableBatch = enableBatch;
566 }
567 }
568
569 public boolean getEnableBatchBooleanValue() {
570 return Boolean.valueOf(enableBatch).booleanValue();
571 }
572
573 public int getMaxMessagesPerBatchIntValue() {
574 return Integer.parseInt(maxMessagesPerBatch);
575 }
576
577 public String getMaxMessagesPerBatch() {
578 return maxMessagesPerBatch;
579 }
580
581 /**
582 *
583 */
584 public void setMaxMessagesPerBatch(String maxMessagesPerBatch) {
585 if (maxMessagesPerBatch != null) {
586 this.maxMessagesPerBatch = maxMessagesPerBatch;
587 }
588 }
589
590 public double getBackOffMultiplier() {
591 if (redeliveryPolicy == null) {
592 return 0;
593 }
594 return redeliveryPolicy.getBackOffMultiplier();
595 }
596
597 public long getInitialRedeliveryDelay() {
598 if (redeliveryPolicy == null) {
599 return 0;
600 }
601 return redeliveryPolicy.getInitialRedeliveryDelay();
602 }
603
604 public int getMaximumRedeliveries() {
605 if (redeliveryPolicy == null) {
606 return 0;
607 }
608 return redeliveryPolicy.getMaximumRedeliveries();
609 }
610
611 public boolean isUseExponentialBackOff() {
612 if (redeliveryPolicy == null) {
613 return false;
614 }
615 return redeliveryPolicy.isUseExponentialBackOff();
616 }
617
618 /**
619 *
620 */
621 public void setBackOffMultiplier(short backOffMultiplier) {
622 lazyCreateRedeliveryPolicy().setBackOffMultiplier(backOffMultiplier);
623 }
624
625 /**
626 *
627 */
628 public void setInitialRedeliveryDelay(long initialRedeliveryDelay) {
629 lazyCreateRedeliveryPolicy().setInitialRedeliveryDelay(initialRedeliveryDelay);
630 }
631
632 /**
633 *
634 */
635 public void setMaximumRedeliveries(int maximumRedeliveries) {
636 lazyCreateRedeliveryPolicy().setMaximumRedeliveries(maximumRedeliveries);
637 }
638
639 /**
640 *
641 */
642 public void setUseExponentialBackOff(boolean useExponentialBackOff) {
643 lazyCreateRedeliveryPolicy().setUseExponentialBackOff(useExponentialBackOff);
644 }
645
646 // don't use getter to avoid causing introspection errors in containers
647 public RedeliveryPolicy redeliveryPolicy() {
648 return redeliveryPolicy;
649 }
650
651 public RedeliveryPolicy lazyCreateRedeliveryPolicy() {
652 if (redeliveryPolicy == null) {
653 redeliveryPolicy = new RedeliveryPolicy();
654 }
655 return redeliveryPolicy;
656 }
657 }