/*
 * @(#)DefaultConnectionStrategy.java
 *
 * Copyright 2001-2002 by Oracle Corporation,
 * 500 Oracle Parkway, Redwood Shores, California, 94065, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Oracle Corporation.
 */

package oracle.jbo.common.ampool;

import java.util.Hashtable;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import oracle.jbo.ApplicationModule;
import oracle.jbo.ApplicationModuleHome;
import oracle.jbo.CSMessageBundle;
import oracle.jbo.JboContext;
import oracle.jbo.JboException;
import oracle.jbo.NotConnectedException;
import oracle.jbo.client.Configuration;
import oracle.jbo.common.Diagnostic;
import oracle.jbo.common.PropertyMetadata;
import oracle.jbo.common.ampool.SessionCookie;

/**
 * A default strategy class for application module creation and connection.
 */
public class DefaultConnectionStrategy implements ConnectionStrategy
{
   // Undocumented properties which are used to communicate between the
   // default connection strategy and an EnvInfoProvider.
   public static final String LAST_EXCEPTION  = "jbo.lastexception";

   static final String RECONNECT_REQUIRED = "jbo.reconnect_required";

   public ApplicationModule createApplicationModule(SessionCookie cookie, EnvInfoProvider envInfo)
   {
      ApplicationModule am = null;

      int numOfRetries = 0;
      if (envInfo != null)
      {
         numOfRetries = envInfo.getNumOfRetries();
      }

      RuntimeException lastException = null;
      while (numOfRetries >= 0)
      {
         numOfRetries--;
         Hashtable environment = cookie.getEnvironment();
         Hashtable clientEnvironment = cookie.getClientEnvironment();

         // merge the two environments
         environment.putAll(clientEnvironment);  
         if (envInfo != null)
         {
            if (lastException != null)
            {
               environment.put(LAST_EXCEPTION, lastException);
            }

            envInfo.modifyInitialContext(environment);
            envInfo.getInfo(null, environment);

            // Do not marshal the lastConnectException.
            environment.remove(LAST_EXCEPTION);
         }

         try
         {
            am = createApplicationModule(environment);
            break;
         }
         catch (RuntimeException rex)
         {
            if (numOfRetries >= 0)
            {
               lastException = rex;
            }
            else
            {
               throw rex;
            }
         }
      }

      return am;
   }

   public ApplicationModule createApplicationModule(Hashtable environment)
   {
      // create a new instance using the connect info
      Context initialContext;

      ApplicationModule appModule = null;
      try
      {
//         if(JboEnvUtil.inJServer())
//         {
//           initialContext = oracle.jbo.server.xml.aurora.AuroraJNDIContextHelper.createContext(environment);
//         }
//         else
//         {
         initialContext = new InitialContext(environment);
//         }
         String applicationModuleClassName = (String)environment.get(Configuration.APPMODULE_JNDI_NAME);
         if(applicationModuleClassName == null)
         {
            applicationModuleClassName = (String)environment.get(APPLICATION_MODULE_CLASS_NAME_PROPERTY);
         }
         ApplicationModuleHome home = (ApplicationModuleHome)initialContext.lookup(applicationModuleClassName);

         appModule = home.create();
      }
      catch (javax.naming.NamingException nex)
      {
         Exception dtl = nex;

         if (nex instanceof NamingException)
         {
            Throwable t = ((NamingException)nex).getRootCause();

            if (t != null)
            {
               dtl = nex;
            }
         }

         JboException jex = new JboException(
                                            CSMessageBundle.class
                                            , CSMessageBundle.EXC_APPMODULE_CREATE
                                            , new Object[0]);

         jex.addToDetails(dtl);
         throw jex;
      }

      return appModule;
   }

   public void connect(ApplicationModule applicationModule, SessionCookie cookie,
                       EnvInfoProvider envInfo)
   {
      if (!applicationModule.getTransaction().isConnected())
      {
         Diagnostic.println("DefaultConnectionStrategy is establishing an application module connection");

         // Give precedence to DB_DATASOURCE_NAME for backward compatibility
         String dsName = (String)cookie.getEnvironment(Configuration.DB_DATASOURCE_PROPERTY);

         if(dsName == null)
         {
            dsName = (String)cookie.getEnvironment(Configuration.JDBC_DS_NAME);
         }

         if (dsName != null && dsName.length() > 0)
         {
            // Backward compatibility. 
            String dsUser = (String)cookie.getEnvironment(Configuration.DB_DATASOURCE_USERNAME_PROPERTY);
            String dsPasswd = (String) cookie.getEnvironment(Configuration.DB_DATASOURCE_PASSWD_PROPERTY);
            applicationModule.getTransaction().connectToDataSource(null,
                                                                   dsName,
                                                                   dsUser,
                                                                   dsPasswd,
                                                                   false);
         }
         else
         {
            // An internal property which may be specified by the EnvInfoProvider.
            // Used to determine the number of times that the connection strategy
            // will attempt to connect before failing.  Please note that the 
            // EnvInfoProvider will be reinvoked upon each connection failure.
            int numOfRetries = 0;
            if (envInfo != null)
            {
               numOfRetries = envInfo.getNumOfRetries();
            }

            RuntimeException lastException = null;

            java.util.Properties jdbcProp = new java.util.Properties();
            String userName = (String)cookie.getEnvironment(DB_USERNAME_PROPERTY);
            String password = (String)cookie.getEnvironment(DB_PASSWORD_PROPERTY);

            if (userName != null)
            {
               jdbcProp.put(Configuration.DB_USERNAME_PROPERTY, userName);
            }


            if (password != null)
            {
               jdbcProp.put(Configuration.DB_PASSWORD_PROPERTY, password);
            }

            //YCHUA Now that EnvInfoProvider also get SECURITY_PRINCIPAL and 
            // SECURITY_CREDENTIALS
            String principal = (String)cookie.getEnvironment(JboContext.SECURITY_PRINCIPAL);
            String credentials = (String)cookie.getEnvironment(JboContext.SECURITY_CREDENTIALS);
            if (principal != null)
            {
               jdbcProp.put(JboContext.SECURITY_PRINCIPAL, principal);
            }

            if (credentials != null)
            {
               jdbcProp.put(JboContext.SECURITY_CREDENTIALS, credentials);
            }

	    String securityEnforceStr = (String)cookie.getEnvironment(PropertyMetadata.ENV_SECURITY_ENFORCE.pName);
	    if (securityEnforceStr != null)
            {
               jdbcProp.put(PropertyMetadata.ENV_SECURITY_ENFORCE.pName, securityEnforceStr);
            }

            while (numOfRetries >= 0)
            {
               numOfRetries--;

               if (envInfo != null)
               {
                  if (lastException != null)
                  {
                     jdbcProp.put(LAST_EXCEPTION, lastException);
                  }

                  queryConnectionInfo(envInfo, jdbcProp);

                  // Do not marshal the lastConnectException.
                  jdbcProp.remove(LAST_EXCEPTION);
               }

               try
               {
                  String connectString = (String)cookie.getEnvironment(DB_CONNECT_STRING_PROPERTY);

                  if (connectionRequired(cookie))
                  {
                    applicationModule.getTransaction().connect(connectString, jdbcProp);
                  }
                  break;
               }
               catch (RuntimeException rex)
               {
                  if (numOfRetries >= 0)
                  {
                     lastException = rex;
                  }
                  else
                  {
                     throw rex;
                  }
               }
            }
         }
      }
   }

   public void reconnect(ApplicationModule applicationModule, SessionCookie cookie,
                         EnvInfoProvider envInfo)
   {
      if ((!applicationModule.getTransaction().isConnected()) && 
          connectionRequired(cookie))
      {
         Diagnostic.println("DefaultConnectionStrategy is re-establishing an application module connection");

         cookie.getUserData().put(RECONNECT_REQUIRED, this);
         try
         {
            applicationModule.getTransaction().reconnect();
         }
         catch (NotConnectedException ncex)
         {
            // If we can't connect then the application module
            // must not have been connected before.  Force a connect.
            connect(applicationModule, cookie, envInfo);
         }
      }
   }

   public void disconnect(ApplicationModule applicationModule, boolean retainState, SessionCookie cookie)
   {
      if(!connectionRequired(cookie))
      {
         if(!retainState)
         {
            applicationModule.resetState(true);
         }
         return;
      }

      Diagnostic.println("DefaultConnectionStrategy is disconnecting an application module connection");
      try
      {
         if (retainState)
         {
            applicationModule.getTransaction().disconnect(true);
         }
         else
         {
            // If the retainState has not been specified then disconnect the
            // application module regardless of the value of the
            // jbo.doconnectionpooling flag.  The retainState flag is only
            // specified when the pool is not managing the application
            // module state.
            applicationModule.getTransaction().disconnect();
         }
      }
      catch (NotConnectedException ncex)
      {
         // Okay.  The application module has already been disconnected.
         
      }
   }

   private void queryConnectionInfo(EnvInfoProvider envInfo, Properties jdbcProp)
   {
      while (true)
      {
         String userName = (String) jdbcProp.get(Configuration.DB_USERNAME_PROPERTY);
         String password = (String) jdbcProp.get(Configuration.DB_PASSWORD_PROPERTY);
            
         if (userName == null || userName.length() == 0)
         {
            if (envInfo.getInfo(Configuration.DB_USERNAME_PROPERTY, jdbcProp) == null)
            {
               break;
            }
            continue;
         }
         if (password == null || password.length() == 0)
         {
            if (envInfo.getInfo(Configuration.DB_PASSWORD_PROPERTY, jdbcProp) == null)
            {
               break;
            }
            continue;
         }
         break;
      }

      // A wildcard request for any/all jdbc properties.  An EnvInfoProvider
      // implementation could simply respond to this request instead of
      // the two more specific requests above.  Leaving the specific requests
      // above in order to maintain backwards compatibility.
      envInfo.getInfo(null, jdbcProp);
   }

   private boolean connectionRequired(SessionCookie cookie)
   {
          // Give precedence to DB_DATASOURCE_NAME for backward compatibility
         String dsName = (String)cookie.getEnvironment(Configuration.DB_DATASOURCE_PROPERTY);

         if(dsName == null)
         {
            dsName = (String)cookie.getEnvironment(Configuration.JDBC_DS_NAME);
         }
         if (dsName == null || dsName.length() > 0)
         {

            String connectString = (String)cookie.getEnvironment(DB_CONNECT_STRING_PROPERTY);
            return (connectString != null && connectString.length() > 0);
         }
         return true;
   }
}
