ResourceAdapterImpl.java

/*
 * IronJacamar, a Java EE Connector Architecture implementation
 * Copyright 2015, Red Hat Inc, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the Eclipse Public License 1.0 as
 * published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse
 * Public License for more details.
 *
 * You should have received a copy of the Eclipse Public License 
 * along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.ironjacamar.core.deploymentrepository;

import org.ironjacamar.core.api.deploymentrepository.ConfigProperty;
import org.ironjacamar.core.api.deploymentrepository.InflowRecovery;
import org.ironjacamar.core.api.deploymentrepository.MessageListener;
import org.ironjacamar.core.api.deploymentrepository.ResourceAdapter;
import org.ironjacamar.core.bootstrapcontext.CloneableBootstrapContext;
import org.ironjacamar.core.spi.bv.BeanValidation;
import org.ironjacamar.core.spi.statistics.StatisticsPlugin;
import org.ironjacamar.core.spi.transaction.TransactionIntegration;
import org.ironjacamar.core.spi.transaction.recovery.XAResourceRecovery;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.resource.spi.ActivationSpec;
import javax.resource.spi.BootstrapContext;
import javax.resource.spi.endpoint.MessageEndpointFactory;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

/**
 * A resource adapter implementation
 * @author <a href="jesper.pedersen@ironjacamar.org">Jesper Pedersen</a>
 */
public class ResourceAdapterImpl implements ResourceAdapter
{
   /** Activated */
   private boolean activated;

   /** The resource adapter */
   private javax.resource.spi.ResourceAdapter resourceAdapter;

   /** The BootstrapContext */
   private BootstrapContext bc;
   
   /** The config properties */
   private Collection<ConfigProperty> configProperties;

   /** The statistics */
   private StatisticsPlugin statistics;

   /** The product name */
   private String productName;
   
   /** The product version */
   private String productVersion;
   
   /** The message listeners */
   private Map<String, ActivationSpecImpl> messageListeners;

   /** Is a 1.6+ archive */
   private boolean is16;

   /** Bean validation */
   private BeanValidation beanValidation;

   /** Bean validation groups */
   private List<String> bvGroups;

   /** Transaction integration */
   private TransactionIntegration transactionIntegration;
   
   /** Active endpoints */
   private Map<Endpoint, InflowRecovery> activeEndpoints;

   /**
    * Constructor
    * @param resourceAdapter The resource adapter
    * @param bc The BootstrapContext
    * @param configProperties The configuration properties
    * @param statistics The statistics
    * @param productName The product name
    * @param productVersion The product version
    * @param messageListeners The message listeners
    * @param is16 Is a 1.6+ archive
    * @param beanValidation Bean validation
    * @param bvGroups The bean validation groups
    * @param ti The transaction integration
    */
   public ResourceAdapterImpl(javax.resource.spi.ResourceAdapter resourceAdapter,
                              BootstrapContext bc,
                              Collection<ConfigProperty> configProperties,
                              StatisticsPlugin statistics,
                              String productName, String productVersion,
                              Map<String, ActivationSpecImpl> messageListeners,
                              boolean is16, BeanValidation beanValidation, List<String> bvGroups,
                              TransactionIntegration ti)
   {
      this.activated = false;
      this.resourceAdapter = resourceAdapter;
      this.bc = bc;
      this.configProperties = configProperties;
      this.statistics = statistics;
      this.productName = productName;
      this.productVersion = productVersion;
      this.messageListeners = messageListeners;
      this.is16 = is16;
      this.beanValidation = beanValidation;
      this.bvGroups = bvGroups;
      this.activeEndpoints = new HashMap<>();
      this.transactionIntegration = ti;
   }
   
   /**
    * {@inheritDoc}
    */
   public boolean isActivated()
   {
      return activated;
   }

   /**
    * {@inheritDoc}
    */
   public javax.resource.spi.ResourceAdapter getResourceAdapter()
   {
      return resourceAdapter;
   }

   /**
    * {@inheritDoc}
    */
   public BootstrapContext getBootstrapContext()
   {
      return bc;
   }

   /**
    * {@inheritDoc}
    */
   public Collection<ConfigProperty> getConfigProperties()
   {
      return configProperties;
   }

   /**
    * {@inheritDoc}
    */
   public StatisticsPlugin getStatistics()
   {
      return statistics;
   }

   /**
    * {@inheritDoc}
    */
   public boolean isMessageListenerSupported(String ml)
   {
      if (messageListeners != null && ml != null)
         return messageListeners.containsKey(ml);

      return false;
   }

   /**
    * {@inheritDoc}
    */
   public MessageListener createMessageListener(String ml) throws Exception
   {
      if (isMessageListenerSupported(ml))
      {
         Class<?> mlClz = Class.forName(ml, true, resourceAdapter.getClass().getClassLoader());
         return new MessageListenerImpl(mlClz, messageListeners.get(ml), resourceAdapter.getClass().getClassLoader());
      }

      throw new Exception(ml + " not supported by " + resourceAdapter.getClass().getName());
   }
   
   /**
    * {@inheritDoc}
    */
   public void endpointActivation(MessageEndpointFactory mef, ActivationSpec as) throws Exception
   {
      if (mef == null)
         throw new Exception("MessageEndpointFactory is null");

      if (as == null)
         throw new Exception("ActivationSpec is null");

      as.setResourceAdapter(resourceAdapter);
      
      try
      {
         as.validate();
      }
      catch (UnsupportedOperationException uoe)
      {
         // Ignore
      }

      if (is16)
      {
         verifyBeanValidation(as);
      }
      
      resourceAdapter.endpointActivation(mef, as);

      InflowRecovery ir = null;
      if (transactionIntegration != null && transactionIntegration.getRecoveryRegistry() != null)
      {
         XAResourceRecovery xar = transactionIntegration.createXAResourceRecovery(resourceAdapter,
                                                                                  as,
                                                                                  productName, productVersion);

         ir = new InflowRecoveryImpl(mef, as, xar, transactionIntegration.getRecoveryRegistry());
         ir.activate();
      }

      activeEndpoints.put(new Endpoint(mef, as), ir);
   }

   /**
    * {@inheritDoc}
    */
   public void endpointDeactivation(MessageEndpointFactory mef, ActivationSpec as) throws Exception
   {
      if (mef == null)
         throw new Exception("MessageEndpointFactory is null");

      if (as == null)
         throw new Exception("ActivationSpec is null");

      Endpoint e = new Endpoint(mef, as);
      InflowRecovery ir = activeEndpoints.get(e);

      if (ir != null)
         ir.deactivate();
      
      try
      {
         resourceAdapter.endpointDeactivation(mef, as);
      }
      finally
      {
         activeEndpoints.remove(e);
      }
   }

   /**
    * {@inheritDoc}
    */
   public Collection<InflowRecovery> getRecovery()
   {
      return Collections.unmodifiableCollection(activeEndpoints.values());
   }

   /**
    * {@inheritDoc}
    */
   public boolean activate() throws Exception
   {
      if (!activated)
      {
         resourceAdapter.start(bc);

         activated = true;
         return true;
      }

      return false;
   }

   /**
    * {@inheritDoc}
    */
   public boolean deactivate() throws Exception
   {
      if (activated)
      {
         if (!activeEndpoints.isEmpty())
            throw new Exception("Still active endpoints");

         resourceAdapter.stop();
         ((CloneableBootstrapContext) bc).shutdown();
         activated = false;
         return true;
      }

      return false;
   }

   /**
    * Verify activation spec against bean validation
    * @param as The activation spec
    * @exception Exception Thrown in case of a violation
    */
   @SuppressWarnings("unchecked")
   private void verifyBeanValidation(Object as) throws Exception
   {
      if (beanValidation != null)
      {
         ValidatorFactory vf = null;

         try
         {
            vf = beanValidation.getValidatorFactory();
            Validator v = vf.getValidator();

            Collection<String> l = bvGroups;
            if (l == null || l.isEmpty())
               l = Arrays.asList(javax.validation.groups.Default.class.getName());

            Collection<Class<?>> groups = new ArrayList<>();
            for (String clz : l)
            {
               groups.add(Class.forName(clz, true, resourceAdapter.getClass().getClassLoader()));
            }
         
            Set failures = v.validate(as, groups.toArray(new Class<?>[groups.size()]));
         
            if (!failures.isEmpty())
            {
               throw new ConstraintViolationException("Violation for " + as, failures);
            }
         }
         finally
         {
            if (vf != null)
               vf.close();
         }
      }
   }

   /**
    * Endpoint
    */
   static class Endpoint
   {
      private MessageEndpointFactory mef;
      private ActivationSpec as;

      /**
       * Constructor
       * @param mef The MessageEndpointFactory
       * @param as The ActivationSpec
       */
      Endpoint(MessageEndpointFactory mef, ActivationSpec as)
      {
         this.mef = mef;
         this.as = as;
      }

      /**
       * {@inheritDoc}
       */
      public int hashCode()
      {
         int hash = 7;
         hash += 7 * mef.hashCode();
         hash += 7 * as.hashCode();
         return hash;
      }

      /**
       * {@inheritDoc}
       */
      public boolean equals(Object o)
      {
         if (o == this)
            return true;

         if (o == null || !(o instanceof Endpoint))
            return false;

         Endpoint e = (Endpoint)o;

         if (mef.equals(e.mef) && as.equals(e.as))
            return true;

         return false;
      }
   }
}