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}