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 */ 017package org.apache.activemq.ra; 018 019import java.beans.IntrospectionException; 020import java.beans.PropertyDescriptor; 021import java.io.Serializable; 022import java.util.ArrayList; 023import java.util.Iterator; 024import java.util.List; 025 026import javax.jms.Queue; 027import javax.jms.Session; 028import javax.jms.Topic; 029import javax.resource.ResourceException; 030import javax.resource.spi.InvalidPropertyException; 031import javax.resource.spi.ResourceAdapter; 032 033import org.apache.activemq.RedeliveryPolicy; 034import org.apache.activemq.command.ActiveMQDestination; 035import org.apache.activemq.command.ActiveMQQueue; 036import org.apache.activemq.command.ActiveMQTopic; 037import 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 * $Date: 2007-08-11 17:29:21 -0400 (Sat, 11 Aug 044 * 2007) $ 045 */ 046public 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 String trustStore; 077 private String trustStorePassword; 078 private String keyStore; 079 private String keyStorePassword; 080 private String keyStoreKeyPassword; 081 private RedeliveryPolicy redeliveryPolicy; 082 private boolean useJndi; 083 084 /** 085 * @see javax.resource.spi.ActivationSpec#validate() 086 */ 087 public void validate() throws InvalidPropertyException { 088 List<String> errorMessages = new ArrayList<String>(); 089 List<PropertyDescriptor> propsNotSet = new ArrayList<PropertyDescriptor>(); 090 try { 091 if (!isValidDestination(errorMessages)) { 092 propsNotSet.add(new PropertyDescriptor("destination", ActiveMQActivationSpec.class)); 093 } 094 if (!isValidDestinationType(errorMessages)) { 095 propsNotSet.add(new PropertyDescriptor("destinationType", ActiveMQActivationSpec.class)); 096 } 097 if (!isValidAcknowledgeMode(errorMessages)) { 098 propsNotSet.add(new PropertyDescriptor("acknowledgeMode", ActiveMQActivationSpec.class)); 099 } 100 if (!isValidSubscriptionDurability(errorMessages)) { 101 propsNotSet.add(new PropertyDescriptor("subscriptionDurability", ActiveMQActivationSpec.class)); 102 } 103 if (!isValidClientId(errorMessages)) { 104 propsNotSet.add(new PropertyDescriptor("clientId", ActiveMQActivationSpec.class)); 105 } 106 if (!isValidSubscriptionName(errorMessages)) { 107 propsNotSet.add(new PropertyDescriptor("subscriptionName", ActiveMQActivationSpec.class)); 108 } 109 if (!isValidMaxMessagesPerSessions(errorMessages)) { 110 propsNotSet.add(new PropertyDescriptor("maxMessagesPerSessions", ActiveMQActivationSpec.class)); 111 } 112 if (!isValidMaxSessions(errorMessages)) { 113 propsNotSet.add(new PropertyDescriptor("maxSessions", ActiveMQActivationSpec.class)); 114 } 115 if (!isValidMessageSelector(errorMessages)) { 116 propsNotSet.add(new PropertyDescriptor("messageSelector", ActiveMQActivationSpec.class)); 117 } 118 if (!isValidNoLocal(errorMessages)) { 119 propsNotSet.add(new PropertyDescriptor("noLocal", ActiveMQActivationSpec.class)); 120 } 121 if (!isValidUseRAManagedTransaction(errorMessages)) { 122 propsNotSet.add(new PropertyDescriptor("useRAManagedTransaction", ActiveMQActivationSpec.class)); 123 } 124 if (!isValidEnableBatch(errorMessages)) { 125 propsNotSet.add(new PropertyDescriptor("enableBatch", ActiveMQActivationSpec.class)); 126 } 127 if (!isValidMaxMessagesPerBatch(errorMessages)) { 128 propsNotSet.add(new PropertyDescriptor("maxMessagesPerBatch", ActiveMQActivationSpec.class)); 129 } 130 131 } catch (IntrospectionException e) { 132 e.printStackTrace(); 133 } 134 135 if (propsNotSet.size() > 0) { 136 StringBuffer b = new StringBuffer(); 137 b.append("Invalid settings:"); 138 for (Iterator<String> iter = errorMessages.iterator(); iter.hasNext();) { 139 b.append(" "); 140 b.append(iter.next()); 141 } 142 InvalidPropertyException e = new InvalidPropertyException(b.toString()); 143 final PropertyDescriptor[] descriptors = propsNotSet.toArray(new PropertyDescriptor[propsNotSet.size()]); 144 e.setInvalidPropertyDescriptors(descriptors); 145 throw e; 146 } 147 } 148 149 public boolean isValidUseRAManagedTransaction(List<String> errorMessages) { 150 try { 151 new Boolean(useRAManagedTransaction); 152 return true; 153 } catch (Throwable e) { 154 // 155 } 156 errorMessages.add("useRAManagedTransaction must be set to: true or false."); 157 return false; 158 } 159 160 public boolean isValidNoLocal(List<String> errorMessages) { 161 try { 162 new Boolean(noLocal); 163 return true; 164 } catch (Throwable e) { 165 // 166 } 167 errorMessages.add("noLocal must be set to: true or false."); 168 return false; 169 } 170 171 public boolean isValidMessageSelector(List<String> errorMessages) { 172 try { 173 if (!isEmpty(messageSelector)) { 174 SelectorParser.parse(messageSelector); 175 } 176 return true; 177 } catch (Throwable e) { 178 errorMessages.add("messageSelector not set to valid message selector: " + e); 179 return false; 180 } 181 } 182 183 public boolean isValidMaxSessions(List<String> errorMessages) { 184 try { 185 if (Integer.parseInt(maxSessions) > 0) { 186 return true; 187 } 188 } catch (NumberFormatException e) { 189 // 190 } 191 errorMessages.add("maxSessions must be set to number > 0"); 192 return false; 193 } 194 195 public boolean isValidMaxMessagesPerSessions(List<String> errorMessages) { 196 try { 197 if (Integer.parseInt(maxMessagesPerSessions) > 0) { 198 return true; 199 } 200 } catch (NumberFormatException e) { 201 // 202 } 203 errorMessages.add("maxMessagesPerSessions must be set to number > 0"); 204 return false; 205 } 206 207 public boolean isValidMaxMessagesPerBatch(List<String> errorMessages) { 208 try { 209 if (Integer.parseInt(maxMessagesPerBatch) > 0) { 210 return true; 211 } 212 } catch (NumberFormatException e) { 213 // 214 } 215 errorMessages.add("maxMessagesPerBatch must be set to number > 0"); 216 return false; 217 } 218 219 public boolean isValidEnableBatch(List<String> errorMessages) { 220 try { 221 new Boolean(enableBatch); 222 return true; 223 } catch (Throwable e) { 224 // 225 } 226 errorMessages.add("enableBatch must be set to: true or false"); 227 return false; 228 } 229 230 public ResourceAdapter getResourceAdapter() { 231 return resourceAdapter; 232 } 233 234 /** 235 * @see javax.resource.spi.ResourceAdapterAssociation#setResourceAdapter(javax.resource.spi.ResourceAdapter) 236 */ 237 public void setResourceAdapter(ResourceAdapter resourceAdapter) throws ResourceException { 238 // spec section 5.3.3 239 if (this.resourceAdapter != null) { 240 throw new ResourceException("ResourceAdapter already set"); 241 } 242 if (!(resourceAdapter instanceof MessageResourceAdapter)) { 243 throw new ResourceException("ResourceAdapter is not of type: " + MessageResourceAdapter.class.getName()); 244 } 245 this.resourceAdapter = (MessageResourceAdapter)resourceAdapter; 246 } 247 248 // /////////////////////////////////////////////////////////////////////// 249 // 250 // Java Bean getters and setters for this ActivationSpec class. 251 // 252 // /////////////////////////////////////////////////////////////////////// 253 public String getDestinationType() { 254 if (!isEmpty(destinationType)) { 255 return destinationType; 256 } 257 return null; 258 } 259 260 /** 261 * @param destinationType The destinationType to set. 262 */ 263 public void setDestinationType(String destinationType) { 264 this.destinationType = destinationType; 265 } 266 267 public String getPassword() { 268 if (!isEmpty(password)) { 269 return password; 270 } 271 return null; 272 } 273 274 /** 275 * 276 */ 277 public void setPassword(String password) { 278 this.password = password; 279 } 280 281 public String getUserName() { 282 if (!isEmpty(userName)) { 283 return userName; 284 } 285 return null; 286 } 287 288 /** 289 * 290 */ 291 public void setUserName(String userName) { 292 this.userName = userName; 293 } 294 295 public String getMessageSelector() { 296 if (!isEmpty(messageSelector)) { 297 return messageSelector; 298 } 299 return null; 300 } 301 302 /** 303 * @param messageSelector The messageSelector to set. 304 */ 305 public void setMessageSelector(String messageSelector) { 306 this.messageSelector = messageSelector; 307 } 308 309 public String getNoLocal() { 310 return noLocal; 311 } 312 313 /** 314 * @param noLocal The noLocal to set. 315 */ 316 public void setNoLocal(String noLocal) { 317 if (noLocal != null) { 318 this.noLocal = noLocal; 319 } 320 } 321 322 public String getAcknowledgeMode() { 323 if (!isEmpty(acknowledgeMode)) { 324 return acknowledgeMode; 325 } 326 return null; 327 } 328 329 /** 330 * 331 */ 332 public void setAcknowledgeMode(String acknowledgeMode) { 333 this.acknowledgeMode = acknowledgeMode; 334 } 335 336 public String getClientId() { 337 if (!isEmpty(clientId)) { 338 return clientId; 339 } 340 return null; 341 } 342 343 /** 344 * 345 */ 346 public void setClientId(String clientId) { 347 this.clientId = clientId; 348 } 349 350 public String getDestination() { 351 if (!isEmpty(destination)) { 352 return destination; 353 } 354 return null; 355 } 356 357 /** 358 * 359 */ 360 public void setDestination(String destination) { 361 this.destination = destination; 362 } 363 364 public String getSubscriptionDurability() { 365 if (!isEmpty(subscriptionDurability)) { 366 return subscriptionDurability; 367 } 368 return null; 369 } 370 371 /** 372 * 373 */ 374 public void setSubscriptionDurability(String subscriptionDurability) { 375 this.subscriptionDurability = subscriptionDurability; 376 } 377 378 public String getSubscriptionName() { 379 if (!isEmpty(subscriptionName)) { 380 return subscriptionName; 381 } 382 return null; 383 } 384 385 /** 386 * 387 */ 388 public void setSubscriptionName(String subscriptionName) { 389 this.subscriptionName = subscriptionName; 390 } 391 392 public boolean isValidSubscriptionName(List<String> errorMessages) { 393 if (!isDurableSubscription() ? true : subscriptionName != null && subscriptionName.trim().length() > 0) { 394 return true; 395 } 396 errorMessages.add("subscriptionName must be set since durable subscription was requested."); 397 return false; 398 } 399 400 public boolean isValidClientId(List<String> errorMessages) { 401 if (!isDurableSubscription() ? true : clientId != null && clientId.trim().length() > 0) { 402 return true; 403 } 404 errorMessages.add("clientId must be set since durable subscription was requested."); 405 return false; 406 } 407 408 public boolean isDurableSubscription() { 409 return DURABLE_SUBSCRIPTION.equals(subscriptionDurability); 410 } 411 412 public boolean isValidSubscriptionDurability(List<String> errorMessages) { 413 // subscriptionDurability only applies to Topics 414 if (DURABLE_SUBSCRIPTION.equals(subscriptionDurability) && getDestinationType() != null && !Topic.class.getName().equals(getDestinationType())) { 415 errorMessages.add("subscriptionDurability cannot be set to: " + DURABLE_SUBSCRIPTION + " when destinationType is set to " + Queue.class.getName() 416 + " as it is only valid when destinationType is set to " + Topic.class.getName() + "."); 417 return false; 418 } 419 if (NON_DURABLE_SUBSCRIPTION.equals(subscriptionDurability) || DURABLE_SUBSCRIPTION.equals(subscriptionDurability)) { 420 return true; 421 } 422 errorMessages.add("subscriptionDurability must be set to: " + NON_DURABLE_SUBSCRIPTION + " or " + DURABLE_SUBSCRIPTION + "."); 423 return false; 424 } 425 426 public boolean isValidAcknowledgeMode(List<String> errorMessages) { 427 if (AUTO_ACKNOWLEDGE_MODE.equals(acknowledgeMode) || DUPS_OK_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) { 428 return true; 429 } 430 errorMessages.add("acknowledgeMode must be set to: " + AUTO_ACKNOWLEDGE_MODE + " or " + DUPS_OK_ACKNOWLEDGE_MODE + "."); 431 return false; 432 } 433 434 public boolean isValidDestinationType(List<String> errorMessages) { 435 if (Queue.class.getName().equals(destinationType) || Topic.class.getName().equals(destinationType)) { 436 return true; 437 } 438 errorMessages.add("destinationType must be set to: " + Queue.class.getName() + " or " + Topic.class.getName() + "."); 439 return false; 440 } 441 442 public boolean isValidDestination(List<String> errorMessages) { 443 if (!(destination == null || destination.equals(""))) { 444 return true; 445 } 446 errorMessages.add("destination is a required field and must be set to the destination name."); 447 return false; 448 } 449 450 public boolean isEmpty(String value) { 451 return value == null || "".equals(value.trim()); 452 } 453 454 /** 455 * 456 */ 457 @Override 458 public String toString() { 459 return "ActiveMQActivationSpec{" + "acknowledgeMode='" + acknowledgeMode + "'" + ", destinationType='" + destinationType + "'" + ", messageSelector='" + messageSelector + "'" 460 + ", destination='" + destination + "'" + ", clientId='" + clientId + "'" + ", subscriptionName='" + subscriptionName + "'" + ", subscriptionDurability='" + subscriptionDurability 461 + "'" + ", useJndi='"+ useJndi + "'" +"}"; 462 } 463 464 public int getAcknowledgeModeForSession() { 465 if (AUTO_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) { 466 return Session.AUTO_ACKNOWLEDGE; 467 } else if (DUPS_OK_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) { 468 return Session.DUPS_OK_ACKNOWLEDGE; 469 } else { 470 return INVALID_ACKNOWLEDGE_MODE; 471 } 472 } 473 474 /** 475 * A helper method mostly for use in Dependency Injection containers which 476 * allows you to customize the destination and destinationType properties 477 * from a single ActiveMQDestination POJO 478 */ 479 public void setActiveMQDestination(ActiveMQDestination destination) { 480 setDestination(destination.getPhysicalName()); 481 if (destination instanceof Queue) { 482 setDestinationType(Queue.class.getName()); 483 } else { 484 setDestinationType(Topic.class.getName()); 485 } 486 } 487 488 /** 489 * 490 */ 491 public ActiveMQDestination createDestination() { 492 if (isEmpty(destinationType) || isEmpty(destination)) { 493 return null; 494 } 495 496 ActiveMQDestination dest = null; 497 if (Queue.class.getName().equals(destinationType)) { 498 dest = new ActiveMQQueue(destination); 499 } else if (Topic.class.getName().equals(destinationType)) { 500 dest = new ActiveMQTopic(destination); 501 } else { 502 assert false : "Execution should never reach here"; 503 } 504 return dest; 505 } 506 507 public String getMaxMessagesPerSessions() { 508 return maxMessagesPerSessions; 509 } 510 511 /** 512 * 513 */ 514 public void setMaxMessagesPerSessions(String maxMessagesPerSessions) { 515 if (maxMessagesPerSessions != null) { 516 this.maxMessagesPerSessions = maxMessagesPerSessions; 517 } 518 } 519 520 public String getMaxSessions() { 521 return maxSessions; 522 } 523 524 /** 525 * 526 */ 527 public void setMaxSessions(String maxSessions) { 528 if (maxSessions != null) { 529 this.maxSessions = maxSessions; 530 } 531 } 532 533 public String getUseRAManagedTransaction() { 534 return useRAManagedTransaction; 535 } 536 537 /** 538 * 539 */ 540 public void setUseRAManagedTransaction(String useRAManagedTransaction) { 541 if (useRAManagedTransaction != null) { 542 this.useRAManagedTransaction = useRAManagedTransaction; 543 } 544 } 545 546 public int getMaxMessagesPerSessionsIntValue() { 547 return Integer.parseInt(maxMessagesPerSessions); 548 } 549 550 public int getMaxSessionsIntValue() { 551 return Integer.parseInt(maxSessions); 552 } 553 554 public boolean isUseRAManagedTransactionEnabled() { 555 return Boolean.valueOf(useRAManagedTransaction).booleanValue(); 556 } 557 558 public boolean getNoLocalBooleanValue() { 559 return Boolean.valueOf(noLocal).booleanValue(); 560 } 561 562 public String getEnableBatch() { 563 return enableBatch; 564 } 565 566 /** 567 * 568 */ 569 public void setEnableBatch(String enableBatch) { 570 if (enableBatch != null) { 571 this.enableBatch = enableBatch; 572 } 573 } 574 575 public boolean getEnableBatchBooleanValue() { 576 return Boolean.valueOf(enableBatch).booleanValue(); 577 } 578 579 public int getMaxMessagesPerBatchIntValue() { 580 return Integer.parseInt(maxMessagesPerBatch); 581 } 582 583 public String getMaxMessagesPerBatch() { 584 return maxMessagesPerBatch; 585 } 586 587 /** 588 * 589 */ 590 public void setMaxMessagesPerBatch(String maxMessagesPerBatch) { 591 if (maxMessagesPerBatch != null) { 592 this.maxMessagesPerBatch = maxMessagesPerBatch; 593 } 594 } 595 596 public double getBackOffMultiplier() { 597 if (redeliveryPolicy == null) { 598 return 0; 599 } 600 return redeliveryPolicy.getBackOffMultiplier(); 601 } 602 603 public long getInitialRedeliveryDelay() { 604 if (redeliveryPolicy == null) { 605 return 0; 606 } 607 return redeliveryPolicy.getInitialRedeliveryDelay(); 608 } 609 610 public int getMaximumRedeliveries() { 611 if (redeliveryPolicy == null) { 612 return 0; 613 } 614 return redeliveryPolicy.getMaximumRedeliveries(); 615 } 616 617 public boolean isUseExponentialBackOff() { 618 if (redeliveryPolicy == null) { 619 return false; 620 } 621 return redeliveryPolicy.isUseExponentialBackOff(); 622 } 623 624 /** 625 * 626 */ 627 public void setBackOffMultiplier(double backOffMultiplier) { 628 lazyCreateRedeliveryPolicy().setBackOffMultiplier(backOffMultiplier); 629 } 630 631 public long getMaximumRedeliveryDelay() { 632 if (redeliveryPolicy == null) { 633 return 0; 634 } 635 return redeliveryPolicy.getMaximumRedeliveryDelay(); 636 } 637 638 public void setMaximumRedeliveryDelay(long maximumRedeliveryDelay) { 639 lazyCreateRedeliveryPolicy().setMaximumRedeliveryDelay(maximumRedeliveryDelay); 640 } 641 642 /** 643 * 644 */ 645 public void setInitialRedeliveryDelay(long initialRedeliveryDelay) { 646 lazyCreateRedeliveryPolicy().setInitialRedeliveryDelay(initialRedeliveryDelay); 647 redeliveryPolicy.setRedeliveryDelay(initialRedeliveryDelay); 648 } 649 650 /** 651 * 652 */ 653 public void setMaximumRedeliveries(int maximumRedeliveries) { 654 lazyCreateRedeliveryPolicy().setMaximumRedeliveries(maximumRedeliveries); 655 } 656 657 /** 658 * 659 */ 660 public void setUseExponentialBackOff(boolean useExponentialBackOff) { 661 lazyCreateRedeliveryPolicy().setUseExponentialBackOff(useExponentialBackOff); 662 } 663 664 // don't use getter to avoid causing introspection errors in containers 665 public RedeliveryPolicy redeliveryPolicy() { 666 return redeliveryPolicy; 667 } 668 669 public RedeliveryPolicy lazyCreateRedeliveryPolicy() { 670 if (redeliveryPolicy == null) { 671 redeliveryPolicy = new RedeliveryPolicy(); 672 } 673 return redeliveryPolicy; 674 } 675 676 public void setUseJndi(boolean useJndi) { 677 this.useJndi = useJndi; 678 } 679 680 public boolean isUseJndi() { 681 return useJndi; 682 } 683 684 public String getTrustStore() { 685 if (!isEmpty(trustStore)) { 686 return trustStore; 687 } 688 return null; 689 } 690 691 public void setTrustStore(String trustStore) { 692 this.trustStore = trustStore; 693 } 694 695 public String getTrustStorePassword() { 696 if (!isEmpty(trustStorePassword)) { 697 return trustStorePassword; 698 } 699 return null; 700 } 701 702 public void setTrustStorePassword(String trustStorePassword) { 703 this.trustStorePassword = trustStorePassword; 704 } 705 706 public String getKeyStore() { 707 if (!isEmpty(keyStore)) { 708 return keyStore; 709 } 710 return null; 711 } 712 713 public void setKeyStore(String keyStore) { 714 this.keyStore = keyStore; 715 } 716 717 public String getKeyStorePassword() { 718 if (!isEmpty(keyStorePassword)) { 719 return keyStorePassword; 720 } 721 return null; 722 } 723 724 public void setKeyStorePassword(String keyStorePassword) { 725 this.keyStorePassword = keyStorePassword; 726 } 727 728 public String getKeyStoreKeyPassword() { 729 if (!isEmpty(keyStoreKeyPassword)) { 730 return keyStoreKeyPassword; 731 } 732 return null; 733 } 734 735 public void setKeyStoreKeyPassword(String keyStoreKeyPassword) { 736 this.keyStoreKeyPassword = keyStoreKeyPassword; 737 } 738}