content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.activemq.ra;
  
  import java.beans.IntrospectionException;
  import java.beans.PropertyDescriptor;
  import java.io.Serializable;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  
  import jakarta.jms.Queue;
  import jakarta.jms.Session;
  import jakarta.jms.Topic;
  import jakarta.resource.ResourceException;
  import jakarta.resource.spi.InvalidPropertyException;
  import jakarta.resource.spi.ResourceAdapter;
  
  import org.apache.activemq.RedeliveryPolicy;
  import org.apache.activemq.command.ActiveMQDestination;
  import org.apache.activemq.command.ActiveMQQueue;
  import org.apache.activemq.command.ActiveMQTopic;
  import org.apache.activemq.selector.SelectorParser;
  
  /**
   * Configures the inbound JMS consumer specification using ActiveMQ
   * 
   * @org.apache.xbean.XBean element="activationSpec"
   *  $Date: 2007-08-11 17:29:21 -0400 (Sat, 11 Aug
   *          2007) $
   */
  public class ActiveMQActivationSpec implements MessageActivationSpec, Serializable {
  
      /** Auto-acknowledge constant for <code>acknowledgeMode</code> property * */
      public static final String AUTO_ACKNOWLEDGE_MODE = "Auto-acknowledge";
      /** Dups-ok-acknowledge constant for <code>acknowledgeMode</code> property * */
      public static final String DUPS_OK_ACKNOWLEDGE_MODE = "Dups-ok-acknowledge";
      /** Durable constant for <code>subscriptionDurability</code> property * */
      public static final String DURABLE_SUBSCRIPTION = "Durable";
      /** NonDurable constant for <code>subscriptionDurability</code> property * */
      public static final String NON_DURABLE_SUBSCRIPTION = "NonDurable";
      public static final int INVALID_ACKNOWLEDGE_MODE = -1;
  
      private static final long serialVersionUID = -7153087544100459975L;
  
      private transient MessageResourceAdapter resourceAdapter;
      private String destinationType;
      private String messageSelector;
      private String destination;
      private String acknowledgeMode = AUTO_ACKNOWLEDGE_MODE;
      private String userName;
      private String password;
      private String clientId;
      private String subscriptionName;
      private String subscriptionDurability = NON_DURABLE_SUBSCRIPTION;
      private String noLocal = "false";
      private String useRAManagedTransaction = "false";
      private String maxSessions = "10";
      private String maxMessagesPerSessions = "10";
      private String enableBatch = "false";
      private String maxMessagesPerBatch = "10";
      private String trustStore;
      private String trustStorePassword;
      private String keyStore;
      private String keyStorePassword;
      private String keyStoreKeyPassword;
      private RedeliveryPolicy redeliveryPolicy;
      private boolean useJndi;
  
      /**
       * @see javax.resource.spi.ActivationSpec#validate()
       */
      public void validate() throws InvalidPropertyException {
          List<String> errorMessages = new ArrayList<String>();
          List<PropertyDescriptor> propsNotSet = new ArrayList<PropertyDescriptor>();
          try {
              if (!isValidDestination(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("destination", ActiveMQActivationSpec.class));
              }
              if (!isValidDestinationType(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("destinationType", ActiveMQActivationSpec.class));
              }
              if (!isValidAcknowledgeMode(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("acknowledgeMode", ActiveMQActivationSpec.class));
              }
              if (!isValidSubscriptionDurability(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("subscriptionDurability", ActiveMQActivationSpec.class));
              }
              if (!isValidClientId(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("clientId", ActiveMQActivationSpec.class));
              }
              if (!isValidSubscriptionName(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("subscriptionName", ActiveMQActivationSpec.class));
              }
              if (!isValidMaxMessagesPerSessions(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("maxMessagesPerSessions", ActiveMQActivationSpec.class));
              }
              if (!isValidMaxSessions(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("maxSessions", ActiveMQActivationSpec.class));
              }
              if (!isValidMessageSelector(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("messageSelector", ActiveMQActivationSpec.class));
              }
              if (!isValidNoLocal(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("noLocal", ActiveMQActivationSpec.class));
              }
              if (!isValidUseRAManagedTransaction(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("useRAManagedTransaction", ActiveMQActivationSpec.class));
              }
              if (!isValidEnableBatch(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("enableBatch", ActiveMQActivationSpec.class));
              }
              if (!isValidMaxMessagesPerBatch(errorMessages)) {
                  propsNotSet.add(new PropertyDescriptor("maxMessagesPerBatch", ActiveMQActivationSpec.class));
              }
  
          } catch (IntrospectionException e) {
              e.printStackTrace();
          }
  
          if (propsNotSet.size() > 0) {
              StringBuffer b = new StringBuffer();
              b.append("Invalid settings:");
              for (Iterator<String> iter = errorMessages.iterator(); iter.hasNext();) {
                  b.append(" ");
                  b.append(iter.next());
              }
              InvalidPropertyException e = new InvalidPropertyException(b.toString());
              final PropertyDescriptor[] descriptors = propsNotSet.toArray(new PropertyDescriptor[propsNotSet.size()]);
              e.setInvalidPropertyDescriptors(descriptors);
              throw e;
          }
      }
  
      public boolean isValidUseRAManagedTransaction(List<String> errorMessages) {
          boolean valid = isValidBooleanString(useRAManagedTransaction); 
          if(!valid) {
              errorMessages.add("useRAManagedTransaction must be set to: true or false.");
          }
          return valid; 
      }
  
      public boolean isValidNoLocal(List<String> errorMessages) {
          boolean valid = isValidBooleanString(noLocal); 
          if(!valid) {
              errorMessages.add("noLocal must be set to: true or false.");
          }
          return valid;
      }
  
      public boolean isValidMessageSelector(List<String> errorMessages) {
          try {
              if (!isEmpty(messageSelector)) {
                  SelectorParser.parse(messageSelector);
              }
              return true;
          } catch (Throwable e) {
              errorMessages.add("messageSelector not set to valid message selector: " + e);
              return false;
          }
      }
  
      public boolean isValidMaxSessions(List<String> errorMessages) {
          try {
              if (Integer.parseInt(maxSessions) > 0) {
                  return true;
              }
          } catch (NumberFormatException e) {
              //
          }
          errorMessages.add("maxSessions must be set to number > 0");
          return false;
      }
  
      public boolean isValidMaxMessagesPerSessions(List<String> errorMessages) {
          try {
              if (Integer.parseInt(maxMessagesPerSessions) > 0) {
                  return true;
              }
          } catch (NumberFormatException e) {
              //
          }
          errorMessages.add("maxMessagesPerSessions must be set to number > 0");
          return false;
      }
  
      public boolean isValidMaxMessagesPerBatch(List<String> errorMessages) {
          try {
              if (Integer.parseInt(maxMessagesPerBatch) > 0) {
                  return true;
              }
          } catch (NumberFormatException e) {
              //
          }
          errorMessages.add("maxMessagesPerBatch must be set to number > 0");
          return false;
      }
  
      public boolean isValidEnableBatch(List<String> errorMessages) {
          boolean valid = isValidBooleanString(enableBatch);
          if(!valid) {
              errorMessages.add("enableBatch must be set to: true or false");
          }
          return valid;
      }
  
      public ResourceAdapter getResourceAdapter() {
          return resourceAdapter;
      }
  
      /**
       * @see javax.resource.spi.ResourceAdapterAssociation#setResourceAdapter(javax.resource.spi.ResourceAdapter)
       */
      public void setResourceAdapter(ResourceAdapter resourceAdapter) throws ResourceException {
          // spec section 5.3.3
          if (this.resourceAdapter != null) {
              throw new ResourceException("ResourceAdapter already set");
          }
          if (!(resourceAdapter instanceof MessageResourceAdapter)) {
              throw new ResourceException("ResourceAdapter is not of type: " + MessageResourceAdapter.class.getName());
          }
          this.resourceAdapter = (MessageResourceAdapter)resourceAdapter;
      }
  
      // ///////////////////////////////////////////////////////////////////////
      //
      // Java Bean getters and setters for this ActivationSpec class.
      //
      // ///////////////////////////////////////////////////////////////////////
      public String getDestinationType() {
          if (!isEmpty(destinationType)) {
              return destinationType;
          }
          return null;
      }
  
      /**
       * @param destinationType The destinationType to set.
       */
      public void setDestinationType(String destinationType) {
          this.destinationType = destinationType;
      }
  
      public String getPassword() {
          if (!isEmpty(password)) {
              return password;
          }
          return null;
      }
  
      /**
       * 
       */
      public void setPassword(String password) {
          this.password = password;
      }
  
      public String getUserName() {
          if (!isEmpty(userName)) {
              return userName;
          }
          return null;
      }
  
      /**
       * 
       */
      public void setUserName(String userName) {
          this.userName = userName;
      }
  
      public String getMessageSelector() {
          if (!isEmpty(messageSelector)) {
              return messageSelector;
          }
          return null;
      }
  
      /**
       * @param messageSelector The messageSelector to set.
       */
      public void setMessageSelector(String messageSelector) {
          this.messageSelector = messageSelector;
      }
  
      public String getNoLocal() {
          return noLocal;
      }
  
      /**
       * @param noLocal The noLocal to set.
       */
      public void setNoLocal(String noLocal) {
          if (noLocal != null) {
              this.noLocal = noLocal;
          }
      }
  
      public String getAcknowledgeMode() {
          if (!isEmpty(acknowledgeMode)) {
              return acknowledgeMode;
          }
          return null;
      }
  
      /**
       * 
       */
      public void setAcknowledgeMode(String acknowledgeMode) {
          this.acknowledgeMode = acknowledgeMode;
      }
  
      public String getClientId() {
          if (!isEmpty(clientId)) {
              return clientId;
          }
          return null;
      }
  
      /**
       * 
       */
      public void setClientId(String clientId) {
          this.clientId = clientId;
      }
  
      public String getDestination() {
          if (!isEmpty(destination)) {
              return destination;
          }
          return null;
      }
  
      /**
       * 
       */
      public void setDestination(String destination) {
          this.destination = destination;
      }
  
      public String getSubscriptionDurability() {
          if (!isEmpty(subscriptionDurability)) {
              return subscriptionDurability;
          }
          return null;
      }
  
      /**
       * 
       */
      public void setSubscriptionDurability(String subscriptionDurability) {
          this.subscriptionDurability = subscriptionDurability;
      }
  
      public String getSubscriptionName() {
          if (!isEmpty(subscriptionName)) {
              return subscriptionName;
          }
          return null;
      }
  
      /**
       * 
       */
      public void setSubscriptionName(String subscriptionName) {
          this.subscriptionName = subscriptionName;
      }
  
      public boolean isValidSubscriptionName(List<String> errorMessages) {
          if (!isDurableSubscription() ? true : subscriptionName != null && subscriptionName.trim().length() > 0) {
              return true;
          }
          errorMessages.add("subscriptionName must be set since durable subscription was requested.");
          return false;
      }
  
      public boolean isValidClientId(List<String> errorMessages) {
          if (!isDurableSubscription() ? true : clientId != null && clientId.trim().length() > 0) {
              return true;
          }
          errorMessages.add("clientId must be set since durable subscription was requested.");
          return false;
      }
  
      public boolean isDurableSubscription() {
          return DURABLE_SUBSCRIPTION.equals(subscriptionDurability);
      }
  
      public boolean isValidSubscriptionDurability(List<String> errorMessages) {
          // subscriptionDurability only applies to Topics
          if (DURABLE_SUBSCRIPTION.equals(subscriptionDurability) && getDestinationType() != null && !Topic.class.getName().equals(getDestinationType())) {
              errorMessages.add("subscriptionDurability cannot be set to: " + DURABLE_SUBSCRIPTION + " when destinationType is set to " + Queue.class.getName()
                                + " as it is only valid when destinationType is set to " + Topic.class.getName() + ".");
              return false;
          }
          if (NON_DURABLE_SUBSCRIPTION.equals(subscriptionDurability) || DURABLE_SUBSCRIPTION.equals(subscriptionDurability)) {
              return true;
          }
          errorMessages.add("subscriptionDurability must be set to: " + NON_DURABLE_SUBSCRIPTION + " or " + DURABLE_SUBSCRIPTION + ".");
          return false;
      }
  
      public boolean isValidAcknowledgeMode(List<String> errorMessages) {
          if (AUTO_ACKNOWLEDGE_MODE.equals(acknowledgeMode) || DUPS_OK_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) {
              return true;
          }
          errorMessages.add("acknowledgeMode must be set to: " + AUTO_ACKNOWLEDGE_MODE + " or " + DUPS_OK_ACKNOWLEDGE_MODE + ".");
          return false;
      }
  
      public boolean isValidDestinationType(List<String> errorMessages) {
          if (Queue.class.getName().equals(destinationType) || Topic.class.getName().equals(destinationType)) {
              return true;
          }
          errorMessages.add("destinationType must be set to: " + Queue.class.getName() + " or " + Topic.class.getName() + ".");
          return false;
      }
  
      public boolean isValidDestination(List<String> errorMessages) {
          if (!(destination == null || destination.equals(""))) {
              return true;
          }
          errorMessages.add("destination is a required field and must be set to the destination name.");
          return false;
      }
  
      public boolean isEmpty(String value) {
          return value == null || "".equals(value.trim());
      }
  
      /**
       * 
       */
      @Override
      public String toString() {
          return "ActiveMQActivationSpec{" + "acknowledgeMode='" + acknowledgeMode + "'" + ", destinationType='" + destinationType + "'" + ", messageSelector='" + messageSelector + "'"
                 + ", destination='" + destination + "'" + ", clientId='" + clientId + "'" + ", subscriptionName='" + subscriptionName + "'" + ", subscriptionDurability='" + subscriptionDurability
                 + "'" + ", useJndi='"+ useJndi + "'" +"}";
      }
  
      public int getAcknowledgeModeForSession() {
          if (AUTO_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) {
              return Session.AUTO_ACKNOWLEDGE;
          } else if (DUPS_OK_ACKNOWLEDGE_MODE.equals(acknowledgeMode)) {
              return Session.DUPS_OK_ACKNOWLEDGE;
          } else {
              return INVALID_ACKNOWLEDGE_MODE;
          }
      }
  
      /**
       * A helper method mostly for use in Dependency Injection containers which
       * allows you to customize the destination and destinationType properties
       * from a single ActiveMQDestination POJO
       */
      public void setActiveMQDestination(ActiveMQDestination destination) {
          setDestination(destination.getPhysicalName());
          if (destination instanceof Queue) {
              setDestinationType(Queue.class.getName());
          } else {
              setDestinationType(Topic.class.getName());
          }
      }
  
      /**
       * 
       */
      public ActiveMQDestination createDestination() {
          if (isEmpty(destinationType) || isEmpty(destination)) {
              return null;
          }
  
          ActiveMQDestination dest = null;
          if (Queue.class.getName().equals(destinationType)) {
              dest = new ActiveMQQueue(destination);
          } else if (Topic.class.getName().equals(destinationType)) {
              dest = new ActiveMQTopic(destination);
          } else {
              assert false : "Execution should never reach here";
          }
          return dest;
      }
  
      public String getMaxMessagesPerSessions() {
          return maxMessagesPerSessions;
      }
  
      /**
       * 
       */
      public void setMaxMessagesPerSessions(String maxMessagesPerSessions) {
          if (maxMessagesPerSessions != null) {
              this.maxMessagesPerSessions = maxMessagesPerSessions;
          }
      }
  
      public String getMaxSessions() {
          return maxSessions;
      }
  
      /**
       * 
       */
      public void setMaxSessions(String maxSessions) {
          if (maxSessions != null) {
              this.maxSessions = maxSessions;
          }
      }
  
      public String getUseRAManagedTransaction() {
          return useRAManagedTransaction;
      }
  
      /**
       * 
       */
      public void setUseRAManagedTransaction(String useRAManagedTransaction) {
          if (useRAManagedTransaction != null) {
              this.useRAManagedTransaction = useRAManagedTransaction;
          }
      }
  
      public int getMaxMessagesPerSessionsIntValue() {
          return Integer.parseInt(maxMessagesPerSessions);
      }
  
      public int getMaxSessionsIntValue() {
          return Integer.parseInt(maxSessions);
      }
  
      public boolean isUseRAManagedTransactionEnabled() {
          return Boolean.valueOf(useRAManagedTransaction);
      }
  
      public boolean getNoLocalBooleanValue() {
          return Boolean.valueOf(noLocal);
      }
  
      public String getEnableBatch() {
          return enableBatch;
      }
  
      /**
       * 
       */
      public void setEnableBatch(String enableBatch) {
          if (enableBatch != null) {
              this.enableBatch = enableBatch;
          }
      }
  
      public boolean getEnableBatchBooleanValue() {
          return Boolean.valueOf(enableBatch);
      }
  
      public int getMaxMessagesPerBatchIntValue() {
          return Integer.parseInt(maxMessagesPerBatch);
      }
  
      public String getMaxMessagesPerBatch() {
          return maxMessagesPerBatch;
      }
  
      /**
       * 
       */
      public void setMaxMessagesPerBatch(String maxMessagesPerBatch) {
          if (maxMessagesPerBatch != null) {
              this.maxMessagesPerBatch = maxMessagesPerBatch;
          }
      }
  
      public double getBackOffMultiplier() {
          if (redeliveryPolicy == null) {
              return 0;
          }
          return redeliveryPolicy.getBackOffMultiplier();
      }
  
      public long getInitialRedeliveryDelay() {
          if (redeliveryPolicy == null) {
              return 0;
          }
          return redeliveryPolicy.getInitialRedeliveryDelay();
      }
  
      public int getMaximumRedeliveries() {
          if (redeliveryPolicy == null) {
              return 0;
          }
          return redeliveryPolicy.getMaximumRedeliveries();
      }
  
      public boolean isUseExponentialBackOff() {
          if (redeliveryPolicy == null) {
              return false;
          }
          return redeliveryPolicy.isUseExponentialBackOff();
      }
  
      /**
       * 
       */
      public void setBackOffMultiplier(double backOffMultiplier) {
          lazyCreateRedeliveryPolicy().setBackOffMultiplier(backOffMultiplier);
      }
      
      public long getMaximumRedeliveryDelay() {
          if (redeliveryPolicy == null) {
              return 0;
          }
          return redeliveryPolicy.getMaximumRedeliveryDelay();
      }
      
      public void setMaximumRedeliveryDelay(long maximumRedeliveryDelay) {
          lazyCreateRedeliveryPolicy().setMaximumRedeliveryDelay(maximumRedeliveryDelay);
      }
  
      /**
       * 
       */
      public void setInitialRedeliveryDelay(long initialRedeliveryDelay) {
          lazyCreateRedeliveryPolicy().setInitialRedeliveryDelay(initialRedeliveryDelay);
          redeliveryPolicy.setRedeliveryDelay(initialRedeliveryDelay);
      }
  
      /**
       * 
       */
      public void setMaximumRedeliveries(int maximumRedeliveries) {
          lazyCreateRedeliveryPolicy().setMaximumRedeliveries(maximumRedeliveries);
      }
  
      /**
       * 
       */
      public void setUseExponentialBackOff(boolean useExponentialBackOff) {
          lazyCreateRedeliveryPolicy().setUseExponentialBackOff(useExponentialBackOff);
      }
  
      // don't use getter to avoid causing introspection errors in containers
      public RedeliveryPolicy redeliveryPolicy() {
          return redeliveryPolicy;
      }
  
      public RedeliveryPolicy lazyCreateRedeliveryPolicy() {
          if (redeliveryPolicy == null) {
              redeliveryPolicy = new RedeliveryPolicy();
          }
          return redeliveryPolicy;
      }
      
      public void setUseJndi(boolean useJndi) {
          this.useJndi = useJndi;
      }
      
      public boolean isUseJndi() {
          return useJndi;
      }
  
      public String getTrustStore() {
          if (!isEmpty(trustStore)) {
              return trustStore;
          }
          return null;
      }
  
      public void setTrustStore(String trustStore) {
          this.trustStore = trustStore;
      }
  
      public String getTrustStorePassword() {
          if (!isEmpty(trustStorePassword)) {
              return trustStorePassword;
          }
          return null;
      }
  
      public void setTrustStorePassword(String trustStorePassword) {
          this.trustStorePassword = trustStorePassword;
      }
  
      public String getKeyStore() {
          if (!isEmpty(keyStore)) {
              return keyStore;
          }
          return null;
      }
  
      public void setKeyStore(String keyStore) {
          this.keyStore = keyStore;
      }
  
      public String getKeyStorePassword() {
          if (!isEmpty(keyStorePassword)) {
              return keyStorePassword;
          }
          return null;
      }
  
      public void setKeyStorePassword(String keyStorePassword) {
          this.keyStorePassword = keyStorePassword;
      }
  
      public String getKeyStoreKeyPassword() {
          if (!isEmpty(keyStoreKeyPassword)) {
              return keyStoreKeyPassword;
          }
          return null;
      }
  
      public void setKeyStoreKeyPassword(String keyStoreKeyPassword) {
          this.keyStoreKeyPassword = keyStoreKeyPassword;
      }
  
      private static boolean isValidBooleanString(String booleanString) {
          return ("true".equalsIgnoreCase(booleanString) || ("false".equalsIgnoreCase(booleanString)));
      }
  }
  