/*
 * Copyright (C) 2011 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * 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 GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General 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.exoplatform.services.jdbc.impl;

import org.exoplatform.container.xml.InitParams;
import org.exoplatform.container.xml.ValueParam;
import org.exoplatform.container.xml.ValuesParam;
import org.exoplatform.services.jdbc.DataSourceProvider;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.transaction.TransactionService;

import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.transaction.TransactionManager;

/**
 * The default implementation of {@link DataSourceProvider}. It allows you
 * to define a data source as managed thanks to the configuration of this 
 * component. When the data source is declared as managed, the {@link DataSource}
 * object will be wrap into a {@link ManagedDataSource}.
 * 
 * @author <a href="mailto:nfilotto@exoplatform.com">Nicolas Filotto</a>
 * @version $Id$
 *
 */
public class DataSourceProviderImpl implements DataSourceProvider
{

   /**
    * Logger
    */
   private static final Log LOG = ExoLogger.getLogger("exo.kernel.component.common.DataSourceProviderImpl");

   /**
    * The name of the parameter to know if the tx has to be checked or not.
    */
   protected static final String PARAM_CHECK_TX = "check-tx-active";

   /**
    * The name of the parameter to know if the data sources are always managed.
    */
   protected static final String PARAM_ALWAYS_MANAGED = "always-managed";

   /**
    * The name of the parameter of all the managed data sources.
    */
   protected static final String PARAM_MANAGED_DS = "managed-data-sources";
 
   /**
    * The transaction manager
    */
   protected final TransactionManager tm;

   /**
    * Indicates if the data source needs to check if a tx is active
    * to decide if the provided connection needs to be managed or not.
    * If it is set to false, the data source will provide only
    * managed connections if the data source itself is managed. 
    */
   protected boolean checkIfTxActive = true;

   /**
    * Indicates that all the data sources are managed
    */
   protected boolean alwaysManaged;

   /**
    * A set of all the data sources that are managed
    */
   protected final Set<String> managedDS = new HashSet<String>(); 
   
   /**
    * The default constructor
    */
   public DataSourceProviderImpl(InitParams params)
   {
      this(params, null);
   }

   /**
    * The default constructor
    */
   public DataSourceProviderImpl(InitParams params, TransactionService tService)
   {
      this.tm = tService == null ? null : tService.getTransactionManager();
      if (params != null)
      {
         ValueParam param = params.getValueParam(PARAM_CHECK_TX);
         if (param != null)
         {
            this.checkIfTxActive = Boolean.valueOf(param.getValue());
         }
         param = params.getValueParam(PARAM_ALWAYS_MANAGED);
         if (param != null && Boolean.valueOf(param.getValue()))
         {
            this.alwaysManaged = true;
            return;
         }
         ValuesParam vp = params.getValuesParam(PARAM_MANAGED_DS);
         if (vp != null && vp.getValues() != null)
         {
            for (Object oValue : vp.getValues())
            {
               String s = (String)oValue;
               StringTokenizer st = new StringTokenizer(s, ",");
               while (st.hasMoreTokens())
               {
                  String dsName = st.nextToken().trim();
                  if (!dsName.isEmpty())
                  {
                     managedDS.add(dsName);                     
                  }
               }
            }
         }
      }
   }
   
   /**
    * @throws NamingException 
    * @see org.exoplatform.services.jdbc.DataSourceProvider#getDataSource(java.lang.String)
    */
   public DataSource getDataSource(String dataSourceName) throws NamingException
   {
      InitialContext ctx = new InitialContext();
      try
      {
         DataSource ds = (DataSource)ctx.lookup(dataSourceName);
         // wrap the data source object if it is managed
         return isManaged(dataSourceName) ? new ManagedDataSource(ds, tm, checkIfTxActive) : ds;
      }
      finally
      {
         try
         {
            ctx.close();
         }
         catch (NamingException e)
         {
            LOG.warn("Failed to close naming context.", e);
         }
      }
   }
   
   /**
    * @see org.exoplatform.services.jdbc.DataSourceProvider#isManaged(java.lang.String)
    */
   public boolean isManaged(String dataSourceName)
   {
      if (alwaysManaged)
      {
         return true;
      }
      else if (managedDS.isEmpty())
      {
         return false;
      }
      return managedDS.contains(dataSourceName);
   }
}
