/*
 * Created on December 11, 2003
 *
 * ManagedConnectionFactoryImpl.java is part of a Resource Adapter to test 
 * J2EE Connector 1.5 as implemented by JOnAS. 
 */
package ersatz.resourceadapter;

import javax.naming.Reference;
import java.io.PrintWriter;
import java.util.Set;
import java.util.Timer;
import java.util.Iterator;
import java.util.Date;
import javax.security.auth.Subject;
import javax.resource.ResourceException;
import javax.resource.NotSupportedException;
import javax.resource.spi.ResourceAllocationException;
import javax.resource.spi.ManagedConnectionFactory;
import javax.resource.spi.ResourceAdapterAssociation;
import javax.resource.spi.ConnectionManager;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ResourceAdapter;
import javax.resource.spi.BootstrapContext;
import javax.resource.spi.security.PasswordCredential;
import java.io.Serializable;

/**
 * @author Bob Kruse
 *
 * used to test the J2EE Connector as implemented by JOnAS.
 *
 */
public class ManagedConnectionFactoryImpl 
	implements ManagedConnectionFactory, ResourceAdapterAssociation,
	Serializable
{
	Reference reference;
    public String defaultUserName="";     // used in ManagedConnectionImpl, ConnectionRequestInfo  
    public String defaultPassword="";     // set by "getter" method

     // loaded by Application Server
    ResourceAdapter ra=null;         
    String ServerName="";
    String PortNumber="";
    String userName="";  
    String password="";
    String protocol="";

    public ConnectionManager cm;              // loaded by ManagedConnectionFactory
    private ManagedConnectionFactoryImpl mcf;
    private ConnectionRequestInfoImpl crii;
    private ResourceAdapterImpl rai=null;     // re-cast at the same time
    private BootstrapContext bootstrapCtx;    // used to test timer
    private boolean raLoaded=false;
	PrintWriter pw;                           // App Server sets to null by default
	boolean forceMatchNull;  // in matchManagedConnections() when debugging
    public String res_auth;  // set by ConnectionFactory.getConnection()
                              // then put into ManagedConnection when created
    String mfId;
    String cName="ManagedConnectionFactoryImpl";
    //
    public ManagedConnectionFactoryImpl()
    {
        crii = new ConnectionRequestInfoImpl();
        pw        = null;
    }

	public Object createConnectionFactory(ConnectionManager connectionmanager)
		throws ResourceException
	{
        cm = connectionmanager;
        Utility.log(cName+".createConnectionFactory cm="+cm);
		Object obj;
		obj = new ConnectionFactoryImpl(this, cm); // creates ConnectionFactory
        return obj;
	}
	/**
	 * 
	 * When the createConnectionFactory method takes no arguments, the resource adapter 
	 * provides a default ConnectionManager instance. This case is used in a non-managed 
	 * application scenario.
	 */
	public Object createConnectionFactory()
		throws ResourceException
	{
        Utility.log(cName+": error - non-managed detected");
        throw new NotSupportedException(
            "A non-managed two-tier application environment is not supported in J2EE testing.");
	}
	public ManagedConnection createManagedConnection(Subject subject, 
                                                      ConnectionRequestInfo CRI)
		throws ResourceException
	{
        ConnectionRequestInfoImpl info = (ConnectionRequestInfoImpl)CRI;
        Utility.log(cName+".createManagedConnection subject="+subject
                    +", cri="+info);
        mcf = this;
        ManagedConnectionImpl mc;
        if (subject!=null) {
            //
            // The application server user's password to the EIS is carried in Subject
            //
            try {
                PasswordCredential pc = Utility.getPasswordCredential(this, subject, info);
                if (pc==null) {
                    Utility.log(cName
                              +".createManagedConnection PasswordCredential = null, Subject not null.");
                } else {
                    if (pc.getUserName()==null || pc.getUserName().length()==0) {
                        Utility.log(cName+".createManagedConnection userName=empty");
                    } else {
                        userName = new String(pc.getUserName());
                        Utility.log(cName+".createManagedConnection"
                        +" PasswordCredential userName="+userName);
                        password = new String(pc.getPassword()); // pc.password is char[]
                        Utility.log(cName+".createManagedConnection"
                        +" PasswordCredential password="+password);
                    }
                }
            } catch (Exception e) {
                Utility.log(cName+".createManagedConnection getPasswordCredential "
                       +"error: e="+e.toString());
            }
        }

        if (info != null) {
            //
            // The client application component user's password is carried in info
            // Use defaults if userName or password is empty
            //
            String s = info.getUserName();
            if (s.length()>0) userName = s;
            else
                Utility.log(cName+".createManagedConnection"
                      +" ConnectionRequestInfo userName="+s+". Use default="+userName);
            Utility.log(cName+".createManagedConnection"
                      +" ConnectionRequestInfo userName="+userName);
            s = info.getPassword();
            if (s.length()>0) password = s;
            else
                Utility.log(cName+".createManagedConnection"
                      +" ConnectionRequestInfo password="+s+". Use default="+password);
            Utility.log(cName+".createManagedConnection"
                      +" ConnectionRequestInfo password="+password);
        }

        if ((subject==null && info==null) || userName=="") {
            //
            // The default user's password is carried in the ManagedConnectionFactory instance
            //
            userName=defaultUserName;
            password=defaultPassword;
            Utility.log(cName+".createManagedConnection default userName="+userName);
            Utility.log(cName+".createManagedConnection default password="+password);
        }
        ConnectionRequestInfoImpl mcCrii = new ConnectionRequestInfoImpl(crii);
        mcCrii.setPassword(password);
        mcCrii.setUserName(userName);
		try {
            //
            //  Create the ManagedConnectionImpl Instance
            //
            mc = new ManagedConnectionImpl(mcCrii);
            mc.setMcf(mcf);
            mc.setRes_Auth(res_auth);
            mc.setLogWriter(pw);
            Utility.log(cName+".createManagedConnection for eis="+rai.getEIS_URL()
                                  +" mc="+mc);
            return mc;
        } catch (Exception ex) {
            Utility.log(cName+".createManagedConnection : error - Exception ex="
                     +ex.getMessage());
            throw new ResourceAllocationException(ex.getMessage());            
        }
	}
    public void setMatchNull(boolean truefalse) {  // nobody calls this
        forceMatchNull=truefalse;
    }
    public ManagedConnection matchManagedConnections(Set connectionSet, 
	                                                 Subject subject, 
	                                                 ConnectionRequestInfo info)
		throws ResourceException
	{
        String mName=".matchManagedConnections";
        Utility.log(cName+mName+" Set="+connectionSet+" Subject="+subject+" Crinfo="+info);
        ManagedConnectionImpl mc = null;
        if (forceMatchNull) {
            Utility.log(cName+mName+" force new connection");
            return mc;  // forces new connection
        } 
        
        ConnectionRequestInfoImpl mcCrii = null;
        ConnectionRequestInfoImpl curCrii = new ConnectionRequestInfoImpl(crii);
        ManagedConnectionImpl wmc = null;
        Iterator it = connectionSet.iterator();
        int cnt=0;
        while (it.hasNext()) {
            Object obj = it.next();
            Utility.log(cName+mName
               +" find next ManagedConnectionImpl in Set cnt="+ (++cnt)+" obj="+obj);
            if (obj instanceof ManagedConnectionImpl) {
                // see if ManagedConnectionImpl is available
                // i.e., no connection handle exists for mc instance
                // i.e., the connection is closed
                // TODO choose one of the above
                wmc = (ManagedConnectionImpl) obj;
                mcCrii=wmc.getCrii();
                if (curCrii.equals(mcCrii)) {
                    if (wmc.getCHandle()==null || wmc.isClosed()) {
                       mc=wmc;
                       Utility.log(cName+mName
                           +" connection handle == null. Set cnt="+cnt+" matched mc="+wmc);
                
                    } else {
                       // error: connection should not exist J2ee 1.0 pg. 32
                       String s = "connection handle should not exist";
                       Utility.log(cName+mName
                           +"error: "+s+". connectionSet cnt="+cnt);
                    }
                }
                
            }
        }
        if (mc==null) 
            Utility.log(cName+mName+" mc=null No match found. Set cnt="+cnt);
        else 
            Utility.log(cName+mName+" match found mc="+mc);
        return mc;
	}
        //
        // The setLogWriter and getLogWriter 
        // the Application Server calls ManagedConnectionImpl.setLogWriter(pw)
        //
	public void setLogWriter(PrintWriter printwriter)
		throws ResourceException
	{
        Utility.log(cName+".setLogWriter mcfi.pw="+pw+" ApplicationServer's PrintWriter="+printwriter);
		if (printwriter!=null) pw = printwriter;
	}

	public PrintWriter getLogWriter()
		throws ResourceException
	{
		return pw;
	}
    public ResourceAdapter getResourceAdapter()
    {
        return ra;
    }
    /** Set the ResourceAdapter association.
     *
     *  @param   ra    ResourceAdapter passed in by Application Server (JOnAS)
     *  @exception     javax.resource.ResourceException                
    **/
    public void setResourceAdapter(ResourceAdapter ra) throws ResourceException
    {
        Utility.log(cName+".setResourceAdapter arg="+ra);
        if (ra==null) {
            String s = "Application Server cannot set ResourceAdapter to null";
            Utility.log(cName+".setResourceAdapter error: "+s);
            throw new ResourceException(s);
        }
        if (!raLoaded)  {
            this.ra = ra;
            rai = (ResourceAdapterImpl)ra;
            crii.setRaName(rai.getEIS_URL());
            raLoaded=true;   // load exactly once
        } else {
            if (this.ra==ra) {
                String s = "setResoruceAdapter called again by the Application "
                          +"Server with same ResourceAdapter value.";
                Utility.log(cName+".setResourceAdapter error: "+s);
                throw new ResourceException(s);
            } else {
                String s = "ResourceAdapter already set to "+this.ra
                          +". Must not change association";
                Utility.log(cName+".setResourceAdapter error: "+s);
                throw new ResourceException(s);
            }
        }    
    }
    //
    // config-property processing "setter" properties
    //
    /** Set the JOnAS config-property..  Default value is "".
     *
     *  @param   c1    String of config-property.
     *                  for testng RA.
    **/
    public void setServerName(String c1)
    {
      ServerName = c1;
      crii.setServerName(ServerName);
      //Utility.log(cName+".setServerName="+ServerName);
    }
    public String getServerName() {
        return ServerName;
    }
    public void setPortNumber(String s)
    {
        PortNumber=s;
        crii.setPortNumber(PortNumber);
        //Utility.log(cName+".setPortNumber="+PortNumber);
    }
    public String getPortNumber()
    {
        return PortNumber;
    }
    public void setDUserName(String s)
    {
        defaultUserName=s;
        crii.setUserName(s);
        //Utility.log(cName+".setDUserName="+defaultUserName);
    }
    public String getDUserName()
    {
        return defaultUserName;
    }
    public void setDPassword(String s)
    {
        defaultPassword=s;
        crii.setPassword(s);
        //Utility.log(cName+".setDPassword="+defaultPassword);
    }
    public String getDPassword()
    {
        return defaultPassword;
    }
    public void setProtocol(String s)
    {
        protocol=s;
        crii.setProtocol(protocol);
        //Utility.log(cName+".setProtocol="+protocol);
    }
    public String getProtocol()
    {
        return protocol;
    }
    public ConnectionManager getCM()
    {
        return cm;
    }
    public String getRes_Auth() {
        return res_auth;
    }
    public void setRes_Auth(String r) {
        res_auth=r;
    }
    //
    // hashCode and equals method used in ConnectionRequestInfo,
    //                                    ManagedConnectionFactory
    public int hashCode() 
    {
        if (mfId == null || mfId.length() == 0) {
            crii.hashCode();
            mfId = crii.getId();
        }
        int hash = mfId.hashCode();
        return hash;
    }
    public boolean equals(Object other) 
    {
        boolean match = false;
        if (other==null) {
            return match;
        } 
        if (other instanceof ManagedConnectionFactoryImpl) {
            ManagedConnectionFactoryImpl otherMcf = (ManagedConnectionFactoryImpl)other;
            try {
                if (hashCode() != otherMcf.hashCode()) {
                    Utility.log(cName+".equals("+otherMcf+") return="+match);
                    return false;
                }
                match = crii.equals(otherMcf.crii);
            } catch (Exception e) {
                Utility.log(cName+".equals("+otherMcf+") Exception="+e);
                return false;
            }
        }
        return match;
    }
    /*  10.3.7
    A resource adapter may not be able to directly create a Timer instance, if it does 
    not have adequate runtime permissions to create threads. This is because the Timer
    instance starts a background thread. In such a case, the resource adapter can 
    instead use the BootstrapContext instance to obtain a Timer instance from the
    application server.
    */
    public boolean isTimerWorking()
    {
        ResourceAdapterImpl rai = (ResourceAdapterImpl)ra;
        try {
            if (rai.bootstrapCtx==null) {
                throw new Exception("bootstrapCtx=null");        
            }
            Timer timer = rai.bootstrapCtx.createTimer();  // get a Timer instance
            Utility.log(cName+".isTimerWorking Timer was created. ");
            return true;
        } catch (Exception e) {
            Utility.log(cName+".isTimerWorking Error: "+e);
            return false;
        }
    }
}
