/*
 * 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.security.provider;

import javax.security.jacc.*;

import java.util.logging.*;
import com.sun.logging.LogDomains;

import java.util.*;
import java.io.File;

/** 
 *  Implementation of jacc PolicyConfigurationFactory class
 * @author Harpreet Singh
 * @author Ron Monzillo
 * @version
 */
public class PolicyConfigurationFactoryImpl 
    extends PolicyConfigurationFactory{

    // Table of ContextId->PolicyConfiguration 
    static Map polConfTable = new HashMap(); 

    private static Logger logger = 
	Logger.getLogger(LogDomains.SECURITY_LOGGER);

    public PolicyConfigurationFactoryImpl(){
    }

   /**
    * This method is used to obtain an instance of the provider specific
    * class that implements the PolicyConfiguration interface that
    * corresponds to the identified policy context within the provider.
    * The methods of the PolicyConfiguration interface are used to
    * define the policy statements of the identified policy context.
    * <P>
    * If at the time of the call, the identified policy context does not
    * exist in the provider, then the policy context will be created
    * in the provider and the Object that implements the context's
    * PolicyConfiguration Interface will be returned. If the state of the
    * identified context is "deleted" or "inService" it will be transitioned to
    * the "open" state as a result of the call. The states in the lifecycle
    * of a policy context are defined by the PolicyConfiguration interface.
    * <P>
    * For a given value of policy context identifier, this method
    * must always return the same instance of PolicyConfiguration
    * and there must be at most one actual instance of a
    * PolicyConfiguration with a given policy context identifier
    * (during a process context).
    * <P>
    * To preserve the invariant that there be at most one
    * PolicyConfiguration object for a given policy context,
    * it may be necessary for this method to be thread safe.
    * <P>
    * @param contextID A String identifying the policy context whose
    * PolicyConfiguration interface is to be returned. The value passed to
    * this parameter must not be null.
    * <P>
    * @param remove A boolean value that establishes whether or not the
    * policy statements of an existing policy context are to be 
    * removed before its PolicyConfiguration object is returned. If the value
    * passed to this parameter is true, the policy statements of  
    * an existing policy context will be removed. If the value is false, 
    * they will not be removed.
    *
    * @return an Object that implements the PolicyConfiguration
    * Interface matched to the Policy provider and corresponding to the
    * identified policy context.
    *
    * @throws java.lang.SecurityException
    * when called by an AccessControlContext that has not been
    * granted the "setPolicy" SecurityPermission.
    *
    * @throws javax.security.jacc.PolicyContextException
    * if the implementation throws a checked exception that has not been
    * accounted for by the getPolicyConfiguration method signature.
    * The exception thrown
    * by the implementation class will be encapsulated (during construction)
    * in the thrown PolicyContextException.
    */
    public PolicyConfiguration getPolicyConfiguration(String contextId, boolean remove)
	throws PolicyContextException {

	PolicyConfigurationImpl.checkSetPolicyPermission();
	
	if(logger.isLoggable(Level.FINE)){
	    logger.fine("JACC Policy Provider: Getting PolicyConfiguration object with id = "+ contextId);	
	}

	PolicyConfiguration pc = (PolicyConfiguration)polConfTable.get(contextId);

	// if the pc is not in the table, see if it was copied into the
        // filesystem (e.g. by the DAS)
	if (pc == null){
	    pc = getPolicyConfigurationImplFromDirectory(contextId,true,remove);
	    if (pc == null) {
		pc = new PolicyConfigurationImpl(contextId);
		polConfTable.put(contextId,pc);
	    }
	} else {
	    // return the policy configuration to the open state, value of
	    // remove will determine if statements are removed
	    ((PolicyConfigurationImpl) pc).initialize(true,remove,false);
	}
	return pc;
    }

   /**
    * This method determines if the identified policy context
    * exists with state "inService" in the Policy provider
    * associated with the factory.
    * <P>
    * @param contextID A string identifying a policy context
    *
    * @return true if the identified policy context exists within the
    * provider and its state is "inService", false otherwise.
    *
    * @throws java.lang.SecurityException
    * when called by an AccessControlContext that has not been
    * granted the "setPolicy" SecurityPermission.
    *
    * @throws javax.security.jacc.PolicyContextException
    * if the implementation throws a checked exception that has not been
    * accounted for by the inService method signature. The exception thrown
    * by the implementation class will be encapsulated (during construction)
    * in the thrown PolicyContextException.
    */
    public boolean inService(String contextID) throws PolicyContextException{
	PolicyConfigurationImpl.checkSetPolicyPermission();
	PolicyConfiguration pc = (PolicyConfiguration)polConfTable.get(contextID);

	// if the pc is not in the table, see if it was copied into the
        // filesystem (e.g. by the DAS)
	if (pc == null) {
	    pc = getPolicyConfigurationImplFromDirectory(contextID,false,false);
	}
	return pc == null ? false : pc.inService();
    }

    // finds pc copied into the filesystem (by DAS) after the repository was 
    // initialized. Will only open pc if remove is true (otherwise pc will 
    // remain in service);

    private static PolicyConfigurationImpl 
	getPolicyConfigurationImplFromDirectory(String contextId, boolean open, boolean remove){
	PolicyConfigurationImpl pci = null;
	File f = new File(PolicyConfigurationImpl.getContextDirectoryName(contextId));
	if (f.exists()) {
	    pci = new PolicyConfigurationImpl(f,open,remove);
	    if (pci != null) {
		polConfTable.put(contextId,pci);
	    }
	}
	return pci;
    }

    // The following package protected methods are needed to support the 
    // PolicyCongigurationImpl class.

    protected static Collection getPolicyConfigurationImpls() {
	return polConfTable.values(); 
    }

    protected static PolicyConfigurationImpl 
	putPolicyConfigurationImpl(String contextID, PolicyConfigurationImpl pc) {
	return (PolicyConfigurationImpl) polConfTable.put(contextID,pc); 
    }

    // does not reopen PC
    protected static PolicyConfigurationImpl getPolicyConfigurationImpl(String contextId) {
	PolicyConfigurationImpl pci = 
	    (PolicyConfigurationImpl)polConfTable.get(contextId); 
	if (pci == null) {
	    // check if pc was copied into the filesystem after the repository 
	    // was initialized (do not open pc or remove policy statements).
	    pci = getPolicyConfigurationImplFromDirectory(contextId,false,false);
	    if (pci == null) {
		logger.log(Level.WARNING,"pc.unknown_policy_context",
			   new Object[]{contextId});
	    }
	}
	return pci;
    }

}
