/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.enterprise.resource;

import java.util.*;

import javax.resource.ResourceException;
import javax.resource.spi.ManagedConnection;
import javax.transaction.*;
import java.util.logging.*;
import com.sun.logging.*;

import javax.naming.Context;
import javax.naming.NamingException;
import com.sun.enterprise.Switch;
import com.sun.enterprise.connectors.*;
import com.sun.enterprise.distributedtx.*;
import com.sun.enterprise.util.i18n.StringManager;



/**
 * this resource pool does not allow sharing
 * A resource is only given out if it is not used by
 * any enterprise bean and it does not have any pending transaction
 *
 * @author Aditya Gore
 */
public class AssocWithThreadResourcePool extends AbstractResourcePool {
    
    private static ThreadLocal<ResourceHandle> localResource =
        new ThreadLocal<ResourceHandle>();

    public AssocWithThreadResourcePool( String poolName ) 
        throws PoolingException 
    {
        super( poolName );
    }
    
    protected ResourceHandle prefetch( ResourceSpec spec,
        ResourceAllocator alloc, Transaction tran) 
    {
        ResourceHandle ar = localResource.get();
        if (ar != null) {
            //synch on ar and do a quick-n-dirty check to see if the local
            //resource is usable at all
            synchronized( ar.lock ) {
                if ( (ar.getThreadId() != Thread.currentThread().getId()) ||
                        ar.hasConnectionErrorOccurred() || 
                        ar.isDirty() || !ar.isAssociated() ) {
                    //we were associated with someone else or resource error 
                    //occurred or resource was disassociated and used by some one else. So evict
                    //NOTE: We do not setAssociated to false here since someone
                    //else has associated this resource to themself. Also, if
                    //the eviction is because of a resourceError, the resource is
                    //not going to be used anyway.
                   
                    localResource.remove();
                    return null;
                }
                
                if (ar.getResourceState().isFree() && 
                        ar.getResourceState().isUnenlisted()) {
                    if (matchConnections) {
                        if (! alloc.matchConnection( ar ) ) {
                            //again, since the credentials of the caller don't match
                            //evict from ThreadLocal
                            //also, mark the resource as unassociated and make this resource
                            //potentially usable
                            localResource.remove();
                            ar.setAssociated( false );
                            if ( monitoringEnabled ) {
                                poolCounters.incrementNumConnNotSuccessfullyMatched();
                            }
                            return null;
                        }
                        if (monitoringEnabled) {
                            poolCounters.incrementNumConnSuccessfullyMatched();
                        }
                    }
            
                    
                    ar.getResourceState().setBusy( true );

                    return ar;
                }
            }
        }

        return null;
    }

    private void setInThreadLocal( ResourceHandle h ) {
        if ( h != null ) {
            h.setThreadId( Thread.currentThread().getId() );
            h.setAssociated( true );
            localResource.set( h );
        }

    }

    protected boolean isResourceUnused(ResourceHandle h){
        return h.getResourceState().isFree() && !h.isAssociated() ;
    }


    // this is the RI getResource() with some modifications
    /**
     * return resource in free list. If none is found, returns null
     *
     */
    synchronized protected ResourceHandle getUnenlistedResource(ResourceSpec spec,
    ResourceAllocator alloc,
    Transaction tran) throws PoolingException {
    
        ResourceHandle result = null;
        result = super.getUnenlistedResource( spec, alloc, tran );

        //If we came here, that's because free doesn't have anything
        //to offer us. This could be because:
        //1. All free resources are associated
        //2. There are no free resources
        //3. We cannot create anymore free resources
        //Handle case 1 here
       
        //DISASSOCIATE
        if ( result == null ) {
            Iterator tempIter = resources.iterator();
            while( tempIter.hasNext() ) {
                ResourceHandle h = (ResourceHandle) tempIter.next();
                synchronized( h.lock ) {
                    //though we are checking resources from within the free list,
                    //we could have a situation where the resource was free upto
                    //this point, put just before we entered the synchronized block, 
                    //the resource "h" got used by the thread that was associating it
                    //so we need to check for isFree also
                    
                    if (h.getResourceState().isUnenlisted() && 
                            h.getResourceState().isFree()) {
                        if (matchConnections) {  
                            if (! alloc.matchConnection(h) ) {
                                if (monitoringEnabled) {
                                    poolCounters.incrementNumConnNotSuccessfullyMatched();
                                }
                                continue;
                            }
                            if ( monitoringEnabled ) {
                                poolCounters.incrementNumConnSuccessfullyMatched();
                            }
                        }    

                        if ( h.hasConnectionErrorOccurred() ) {
                            continue;
                        }
                        result = h;
                        result.getResourceState().setBusy( true );
                        result.setAssociated( false );
                        break;
                    }
                }
            }
        }
       
        if ( localResource.get() == null ) {
            setInThreadLocal( result );
        }
        return result;
        
    }

    protected synchronized void freeUnenlistedResource(ResourceHandle h) {
        if ( ! h.isAssociated() ) {
            free.add( h );
        }
        notifyWaitingThreads();
    }

    protected void destroyResource(ResourceHandle resourceHandle) {
        try {
            super.destroyResource( resourceHandle );
        } finally { 
            //Note: here we are using the connectionErrorOccurred flag to indicate
            //that this resource is no longer usable. This flag would be checked while
            //getting from ThreadLocal
            //The main intention of marking this is to handle the case where 
            //failAllConnections happens
            //Note that setDirty only happens here - i.e during destroying of a 
            //resource
            
            synchronized( resourceHandle.lock ) {
                resourceHandle.setDirty();
            }
        }
    }

}
