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 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(useRAManagedTransaction); 146 return true; 147 } catch (Throwable e) { 148 // 149 } 150 errorMessages.add("useRAManagedTransaction 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); 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(double backOffMultiplier) { 622 lazyCreateRedeliveryPolicy().setBackOffMultiplier(backOffMultiplier); 623 } 624 625 public long getMaximumRedeliveryDelay() { 626 if (redeliveryPolicy == null) { 627 return 0; 628 } 629 return redeliveryPolicy.getMaximumRedeliveryDelay(); 630 } 631 632 public void setMaximumRedeliveryDelay(long maximumRedeliveryDelay) { 633 lazyCreateRedeliveryPolicy().setMaximumRedeliveryDelay(maximumRedeliveryDelay); 634 } 635 636 /** 637 * 638 */ 639 public void setInitialRedeliveryDelay(long initialRedeliveryDelay) { 640 lazyCreateRedeliveryPolicy().setInitialRedeliveryDelay(initialRedeliveryDelay); 641 } 642 643 /** 644 * 645 */ 646 public void setMaximumRedeliveries(int maximumRedeliveries) { 647 lazyCreateRedeliveryPolicy().setMaximumRedeliveries(maximumRedeliveries); 648 } 649 650 /** 651 * 652 */ 653 public void setUseExponentialBackOff(boolean useExponentialBackOff) { 654 lazyCreateRedeliveryPolicy().setUseExponentialBackOff(useExponentialBackOff); 655 } 656 657 // don't use getter to avoid causing introspection errors in containers 658 public RedeliveryPolicy redeliveryPolicy() { 659 return redeliveryPolicy; 660 } 661 662 public RedeliveryPolicy lazyCreateRedeliveryPolicy() { 663 if (redeliveryPolicy == null) { 664 redeliveryPolicy = new RedeliveryPolicy(); 665 } 666 return redeliveryPolicy; 667 } 668}