/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, 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/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
package com.sun.web.security;

import java.security.*;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.Collections;
import java.lang.*;
import java.net.URL;

import javax.servlet.http.HttpServletRequest;
import javax.security.jacc.*;

import java.util.logging.*;
import java.util.HashMap;
import java.util.Map;
import com.sun.logging.LogDomains;
import com.sun.web.security.WebPermissionUtil;
import com.sun.enterprise.deployment.WebBundleDescriptor;
import com.sun.enterprise.security.AppservAccessController;
import com.sun.enterprise.security.authorize.*;
import com.sun.enterprise.security.CachedPermission;
import com.sun.enterprise.security.CachedPermissionImpl;
import com.sun.enterprise.security.PermissionCache;
import com.sun.enterprise.security.PermissionCacheFactory;
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.audit.AuditManager;
import com.sun.enterprise.security.audit.AuditManagerFactory;
import com.sun.enterprise.security.authorize.PolicyContextHandlerImpl;
import com.sun.enterprise.web.VirtualServer;
import com.sun.enterprise.deployment.runtime.common.SecurityRoleMapping;
import com.sun.enterprise.deployment.PrincipalImpl;
import com.sun.enterprise.deployment.Group;
import com.sun.enterprise.config.serverbeans.*;
import com.sun.enterprise.config.*;
import com.sun.enterprise.server.ApplicationServer;
import com.sun.enterprise.deployment.web.LoginConfiguration;
import com.sun.enterprise.deployment.runtime.web.SunWebApp;
import com.sun.enterprise.deployment.interfaces.SecurityRoleMapperFactory;
import com.sun.enterprise.deployment.interfaces.SecurityRoleMapperFactoryMgr;
import com.sun.enterprise.security.factory.SecurityManagerFactory;
import org.apache.catalina.Globals;

/**
 * The class implements the JSR 115 - JavaTM Authorization Contract for Containers.
 * This class is a companion class of EJBSecurityManager.
 *
 * All the security decisions required to allow access to a resource are defined 
 * in that class.
 *
 * @author Jean-Francois Arcand
 * @author Harpreet Singh.
 * @todo introduce a new class called AbstractSecurityManager. Move functionality
 * from this class and EJBSecurityManager class and extend this class from 
 * AbstractSecurityManager
 */
public class WebSecurityManager extends SecurityManagerFactory {

    private static Logger logger =
            Logger.getLogger(LogDomains.SECURITY_LOGGER);
    private static AuditManager auditManager =
            AuditManagerFactory.getAuditManagerInstance();
    private static final String RESOURCE = "hasResourcePermission";
    private static final String USERDATA = "hasUserDataPermission";
    private static final String ROLEREF = "hasRoleRefPermission";
    private static final String DEFAULT_PATTERN = "/";
    private static final String EMPTY_STRING = "";
    private static final PolicyContextHandlerImpl pcHandlerImpl =
            (PolicyContextHandlerImpl) PolicyContextHandlerImpl.getInstance();
    private static final Map ADMIN_PRINCIPAL = new HashMap();
    private static final Map ADMIN_GROUP = new HashMap();

    // The context ID associated with this instance. This is the name
    // of the application
    private String CONTEXT_ID = null;
    private String CODEBASE = null;
    // The JACC policy provider.
    protected Policy policy = Policy.getPolicy();
    protected PolicyConfiguration pc = null;
    protected static PolicyConfigurationFactory pcf = null;
    protected CodeSource codesource = null;

    // protection domain cache
    private Map protectionDomainCache =
            Collections.synchronizedMap(new WeakHashMap());
    private static WebResourcePermission allResources =
            new WebResourcePermission("/*", (String) null);
    private static WebUserDataPermission allConnections =
            new WebUserDataPermission("/*", null);
    private static Permission[] protoPerms = {
        allResources,
        allConnections
    };

    // permissions tied to unchecked permission cache, and used
    // to determine if the effective policy is grant all
    // WebUserData and WebResource permisions.
    private CachedPermission allResourcesCP = null;
    private CachedPermission allConnectionsCP = null;
    // unchecked permission cache
    private PermissionCache uncheckedPermissionCache = null;
    private static Set defaultPrincipalSet =
            SecurityContext.getDefaultSecurityContext().getPrincipalSet();
    private static SecurityRoleMapperFactory factory =
            SecurityRoleMapperFactoryMgr.getFactory();

    // WebBundledescriptor
    private WebBundleDescriptor wbd = null;
    // Create a WebSecurityObject

    private WebSecurityManager(WebBundleDescriptor wbd) throws PolicyContextException {
        this.wbd = wbd;
        this.CONTEXT_ID = getContextID(wbd);
        String appname = getAppId();
        factory.setAppNameForContext(appname, CONTEXT_ID);
        initialise(appname);
    }

    private String removeSpaces(String withSpaces) {
        return withSpaces.replace(' ', '_');
    }
    // fix for CR 6155144
    // used to get the policy context id. Also used by the RealmAdapter

    public static String getContextID(WebBundleDescriptor wbd) {
        String cid = null;
        if (wbd != null) {
            String moduleId = wbd.getUniqueFriendlyId();
            cid = wbd.getApplication().getRegistrationName() +
                    '/' + wbd.getUniqueFriendlyId();
        }
        return cid;
    }

    private void initialise(String appName) throws PolicyContextException {
        CODEBASE = removeSpaces(CONTEXT_ID);
        if (VirtualServer.ADMIN_VS.equals(getVirtualServers(appName))) {
            LoginConfiguration lgConf = wbd.getLoginConfiguration();
            if (lgConf != null) {
                String realmName = lgConf.getRealmName();
                SunWebApp sunDes = wbd.getSunDescriptor();
                if (sunDes != null) {
                    SecurityRoleMapping[] sr = sunDes.getSecurityRoleMapping();
                    if (sr != null) {
                        for (int i = 0; i < sr.length; i++) {
                            String[] principal = sr[i].getPrincipalName();
                            if (principal != null) {
                                for (int plen = 0; plen < principal.length; plen++) {
                                    ADMIN_PRINCIPAL.put(realmName + principal[plen], new PrincipalImpl(principal[plen]));
                                }
                            }
                            List<String> groups = sr[i].getGroupNames();
                            for (int glen = 0; glen < groups.size(); glen++) {
                                ADMIN_GROUP.put(realmName + groups.get(glen), new Group(groups.get(glen)));
                            }
                        }
                    }
                }
            }
        }

        // will require stuff in hash format for reference later on.
        try {
            java.net.URI uri = null;
            try {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "[Web-Security] Creating a Codebase URI with = " + CODEBASE);
                }
                uri = new java.net.URI("file:///" + CODEBASE);
                if (uri != null) {
                    codesource = new CodeSource(new URL(uri.toString()),
                            (java.security.cert.Certificate[]) null);
                }

            } catch (java.net.URISyntaxException use) {
                // manually create the URL
                logger.log(Level.FINE, "[Web-Security] Error Creating URI ", use);
                throw new RuntimeException(use);
            }

        } catch (java.net.MalformedURLException mue) {
            logger.log(Level.SEVERE, "ejbsm.codesourceerror", mue);
            throw new RuntimeException(mue);
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.fine("[Web-Security] Context id (id under which  WEB component in application will be created) = " + CONTEXT_ID);
            logger.fine("[Web-Security] Codebase (module id for web component) " + CODEBASE);
        }

        loadPolicyConfiguration();

        if (uncheckedPermissionCache == null) {
            uncheckedPermissionCache =
                    PermissionCacheFactory.createPermissionCache(this.CONTEXT_ID, codesource, protoPerms, null);

            allResourcesCP =
                    new CachedPermissionImpl(uncheckedPermissionCache,
                    allResources);
            allConnectionsCP =
                    new CachedPermissionImpl(uncheckedPermissionCache,
                    allConnections);
        } else {
            uncheckedPermissionCache.reset();
        }

    }

    public void loadPolicyConfiguration() throws PolicyContextException {

        boolean inService = getPolicyFactory().inService(CONTEXT_ID);

        // only regenerate policy file if it isn't already in service
        // Consequently all things that deploy modules (as apposed to
        // loading already deployed modules) must make sure pre-exiting
        // pc is either in deleted or open state before this method
        // (i.e. initialise) is called. That is, before constructing
        // the WebSecurityManager. Note that policy statements are not
        // removed to allow multiple web modules to be represented by same pc.

        if (!inService) {

            pc = getPolicyFactory().getPolicyConfiguration(CONTEXT_ID, false);

            try {
                WebPermissionUtil.processConstraints(wbd, pc);
                WebPermissionUtil.createWebRoleRefPermission(wbd, pc);
            } catch (PolicyContextException pce) {
                logger.log(Level.FINE, "[Web-Security] FATAL Permission Generation: " + pce.getMessage());
                throw pce;
            }
        }

    }

    // this will change too - get the application id name
    private String getAppId() {
        return wbd.getApplication().getRegistrationName();
    }

    boolean permitAll(HttpServletRequest req) {
        boolean ret = false;
        WebResourcePermission webResPerm = createWebResourcePermission(req);
        if (uncheckedPermissionCache != null) {
            ret = uncheckedPermissionCache.checkPermission(webResPerm);
        }
        if (ret == false) {
            ret = checkPermissionWithoutCache(webResPerm, null);
        }
        return ret;
    }

    /*
     * Invoke the <code>Policy</code> to determine if the <code>Permission</code>
     * object has security permission.
     * @param perm an instance of <code>Permission</code>.
     * @param principalSet a set containing the principals to check for authorization
     * @return true if granted, false if denied.
     */
    protected boolean checkPermission(Permission perm, Set principalSet) {

        boolean ret = false;
        if (uncheckedPermissionCache != null) {
            ret = uncheckedPermissionCache.checkPermission(perm);
        }
        if (ret == false) {
            ret = checkPermissionWithoutCache(perm, principalSet);
        } else {
            try {
                setPolicyContext(CONTEXT_ID);
            } catch (Throwable t) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE,
                            "[Web-Security] Web Permission Access Denied.", t);
                }
                ret = false;
            }
        }
        return ret;
    }

    private boolean checkPermissionWithoutCache(
            Permission perm, Set principalSet) {

        try {

            // NOTE: there is an assumption here, that this setting of the PC will
            // remain in affect through the component dispatch, and that the
            // component will not call into any other policy contexts.
            // even so, could likely reset on failed check.

            setPolicyContext(CONTEXT_ID);

        } catch (Throwable t) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE,
                        "[Web-Security] Web Permission Access Denied.", t);
            }
            return false;
        }

        ProtectionDomain prdm =
                (ProtectionDomain) protectionDomainCache.get(principalSet);

        if (prdm == null) {

            Principal[] principals = null;
            principals = (principalSet == null ? null : (Principal[]) principalSet.toArray(new Principal[0]));

            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, "[Web-Security] Generating a protection domain for Permission check.");

                if (principals != null) {
                    for (int i = 0; i < principals.length; i++) {
                        logger.log(Level.FINE, "[Web-Security] Checking with Principal : " + principals[i].toString());
                    }
                } else {
                    logger.log(Level.FINE, "[Web-Security] Checking with Principals: null");
                }
            }

            prdm = new ProtectionDomain(codesource, null, null, principals);
            protectionDomainCache.put(principalSet, prdm);
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "[Web-Security] Codesource with Web URL: " + codesource.getLocation().toString());
            logger.log(Level.FINE, "[Web-Security] Checking Web Permission with Principals : " + principalSetToString(principalSet));
            logger.log(Level.FINE, "[Web-Security] Web Permission = " + perm.toString());
        }

        return policy.implies(prdm, perm);
    }

    // obtains PolicyConfigurationFactory once for class
    private static synchronized PolicyConfigurationFactory getPolicyFactory()
            throws PolicyContextException {
        if (pcf == null) {
            try {
                pcf = PolicyConfigurationFactory.getPolicyConfigurationFactory();
            } catch (ClassNotFoundException cnfe) {
                logger.severe("jaccfactory.notfound");
                throw new PolicyContextException(cnfe);
            } catch (PolicyContextException pce) {
                logger.severe("jaccfactory.notfound");
                throw pce;
            }
        }
        return pcf;
    }

    private WebResourcePermission createWebResourcePermission(
            HttpServletRequest httpsr) {
        String uri = (String) httpsr.getAttribute(Globals.CONSTRAINT_URI);
        if (uri == null) {
            uri = httpsr.getRequestURI();
            if (uri != null) {
                 // FIX TO BE CONFIRMED: subtract the context path
                 String contextPath = httpsr.getContextPath();
                 int contextLength = contextPath == null ? 0 :
                     contextPath.length();
                 if (contextLength > 0) {
                     uri = uri.substring(contextLength);
                 }
             }
        }
        if (uri == null) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, "[Web-Security] mappedUri is null");
            }
            throw new RuntimeException("Fatal Error in creating WebResourcePermission");
        }
        if (uri.equals("/")) {
            uri = EMPTY_STRING;
        } else {
 	    // FIX TO BE CONFIRMED: encode all colons
 	    uri = uri.replaceAll(":","%3A");
        }
        WebResourcePermission perm = new WebResourcePermission(
                uri, httpsr.getMethod());
        return perm;
    }

    /**
     * Perform access control based on the <code>HttpServletRequest</code>.
     * Return <code>true</code> if this constraint is satisfied and processing
     * should continue, or <code>false</code> otherwise.
     * @return true is the resource is granted, false if denied
     */
    public boolean hasResourcePermission(HttpServletRequest httpsr) {
        SecurityContext sc = getSecurityContext(httpsr.getUserPrincipal());
        WebResourcePermission perm = createWebResourcePermission(httpsr);
        setSecurityInfo(httpsr);
        boolean isGranted = checkPermission(perm, sc.getPrincipalSet());
        SecurityContext.setCurrent(sc);
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "[Web-Security] hasResource isGranted: " + isGranted);
            logger.log(Level.FINE, "[Web-Security] hasResource perm: " + perm);
        }
        if (auditManager.isAuditOn()) {
            Principal prin = httpsr.getUserPrincipal();
            String user = (prin != null) ? prin.getName() : null;
            auditManager.webInvocation(user, httpsr, RESOURCE, isGranted);
        }
        return isGranted;
    }

    /* 
     * Return <code>true</code> if the specified servletName has the specified
     * security role, within the context of the WebRoleRefPermission; 
     * otherwise return
     * <code>false</code>.
     *
     * @param principal servletName the resource's name.
     * @param principal Principal for whom the role is to be checked
     * @param role Security role to be checked
     * @return true is the resource is granted, false if denied
     */
    public boolean hasRoleRefPermission(String servletName, String role, Principal p) {
        Set principalSet = getSecurityContext(p).getPrincipalSet();
        WebRoleRefPermission perm = new WebRoleRefPermission(servletName, role);
        boolean isGranted = checkPermission(perm, principalSet);
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "[Web-Security] hasRoleRef perm: " + perm);
            logger.log(Level.FINE, "[Web-Security] hasRoleRef isGranted: " + isGranted);
        }
        return isGranted;
    }

    /*
     * Enforce any user data constraint required by the security constraint
     * guarding this request URI.  Return <code>true</code> if this constraint
     * was not violated and processing should continue, or <code>false</code>
     * if we have created a response already.
     *
     * @return true is the user is granted, false if denied.
     */
    public int hasUserDataPermission(HttpServletRequest httpsr) {
        setSecurityInfo(httpsr);
        WebUserDataPermission perm = new WebUserDataPermission(httpsr);
        boolean isGranted = checkPermission(perm, defaultPrincipalSet);
        int result = 0;

        if (isGranted) {
            result = 1;
        }

        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "[Web-Security] hasUserDataPermission perm: " + perm);
            logger.log(Level.FINE, "[Web-Security] hasUserDataPermission isGranted: " + isGranted);
        }

        if (auditManager.isAuditOn()) {
            Principal prin = httpsr.getUserPrincipal();
            String user = (prin != null) ? prin.getName() : null;
            auditManager.webInvocation(user, httpsr, USERDATA, isGranted);
        }

        if (!isGranted) {

            perm = new WebUserDataPermission(perm.getName(),
                    new String[]{httpsr.getMethod()},
                    "CONFIDENTIAL");

            isGranted = checkPermission(perm, defaultPrincipalSet);

            if (isGranted) {
                result = -1;
            }
        }

        return result;
    }

    public void destroy() throws PolicyContextException {
        boolean wasInService = getPolicyFactory().inService(CONTEXT_ID);
        getPolicyFactory().getPolicyConfiguration(CONTEXT_ID, true);
        if (wasInService) {
            policy.refresh();
            PermissionCacheFactory.removePermissionCache(uncheckedPermissionCache);
            uncheckedPermissionCache = null;
        }
        factory.removeAppNameForContext(CONTEXT_ID);
        WebSecurityManager wsm = WebSecurityManager.getManager(CONTEXT_ID, null, true);
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "WebSecurityManager: destroyed" + wsm);
        }
    }

    private static String setPolicyContext(final String ctxID) throws Throwable {
        String old = PolicyContext.getContextID();
        if (old != ctxID &&
                (old == null || ctxID == null || !old.equals(ctxID))) {

            if (logger.isLoggable(Level.FINE)) {
                logger.fine("[Web-Security] Setting Policy Context ID: old = " + old +
                        " ctxID = " + ctxID);
            }

            try {
                AppservAccessController.doPrivileged(new PrivilegedExceptionAction() {

                    public java.lang.Object run() throws Exception {
                        PolicyContext.setContextID(ctxID);
                        return null;
                    }
                });
            } catch (java.security.PrivilegedActionException pae) {
                Throwable cause = pae.getCause();
                if (cause instanceof java.security.AccessControlException) {
                    logger.log(Level.SEVERE, "[Web-Security] setPolicy SecurityPermission required to call PolicyContext.setContextID", cause);
                } else {
                    logger.log(Level.SEVERE, "[Web-Security] Unexpected Exception while setting policy context", cause);
                }
                throw cause;
            }
        } else if (logger.isLoggable(Level.FINE)) {
            logger.fine("[Web-Security] Policy Context ID was: " + old);
        }
        return old;
    }

    /**
     * This is an private method for transforming principal into a SecurityContext
     * @param principal expected to be a WebPrincipal
     * @return SecurityContext
     */
    private SecurityContext getSecurityContext(Principal principal) {
        SecurityContext secContext = null;
        if (principal != null) {
            if (principal instanceof WebPrincipal) {
                WebPrincipal wp = (WebPrincipal) principal;
                secContext = wp.getSecurityContext();
            } else {
                secContext = new SecurityContext(principal.getName(), null);
            }
        }
        if (secContext == null) {
            secContext = SecurityContext.getDefaultSecurityContext();
        }
        return secContext;
    }

    /**
     * This is an private method for policy context handler data info
     * @param httpRequest
     */
    private void setSecurityInfo(HttpServletRequest httpRequest) {
        if (httpRequest != null) {
            pcHandlerImpl.getHandlerData().setHttpServletRequest(httpRequest);
        }
    }

    private String principalSetToString(Set principalSet) {
        String result = null;
        if (principalSet != null) {
            Principal[] principals =
                    (Principal[]) principalSet.toArray(new Principal[0]);
            for (int i = 0; i < principals.length; i++) {
                if (i == 0) {
                    result = principals[i].toString();
                } else {
                    result = result + ", " + new String(principals[i].toString());
                }
            }
        }
        return result;
    }

    private String getVirtualServers(String appName) {
        String ret = null;
        try {
            ConfigContext ctx =
                    ApplicationServer.getServerContext().getConfigContext();
            ret = ServerBeansFactory.getVirtualServersByAppName(ctx, appName);
        } catch (ConfigException ce) {
            logger.log(Level.FINE, "Cannot get virtual server for " + appName, ce);
        }
        return ret;
    }

    public static Principal getAdminPrincipal(String username, String realmName) {
        return (Principal) ADMIN_PRINCIPAL.get(realmName + username);
    }

    public static Principal getAdminGroup(String group, String realmName) {
        return (Principal) ADMIN_GROUP.get(realmName + group);
    }

    /**
     * returns true to indicate that a policy check was made
     * and there were no constrained resources.
     * when caching is disabled must always return false, which will
     * ensure that policy is consulted to authorize each request.
     */
    public boolean hasNoConstrainedResources() {
        if (allResourcesCP != null && allConnectionsCP != null) {
            boolean x = allResourcesCP.checkPermission();
            boolean y = allConnectionsCP.checkPermission();
            return x && y;
        }
        return false;
    }

    // stores the context ids to appnames for standalone web apps
    static private Map<String, ArrayList<String>> CONTEXT_IDS =
            new HashMap<String, ArrayList<String>>();
    static private Map<String, Map<String, WebSecurityManager>> SECURITY_MANAGERS =
            new HashMap<String, Map<String, WebSecurityManager>>();

    public static <T> WebSecurityManager getManager(String ctxId, String name,
            boolean remove) {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "getManager before printcache" + remove);
            printCache();
        }
        WebSecurityManager wsm = getManager(SECURITY_MANAGERS, ctxId, name, remove);
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "getManager after printcache" + remove);
            printCache();
        }
        return wsm;

    }

    public static <T> ArrayList<WebSecurityManager> getManagers(String ctxId, boolean remove) {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "getManagers before printcache" + remove);
            printCache();
        }
        ArrayList<WebSecurityManager> wslist = getManagers(SECURITY_MANAGERS, ctxId, remove);
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "getManagers after printcache" + remove);
            printCache();
        }
        return wslist;
    }

    public static <T> ArrayList<WebSecurityManager> getManagersForApp(String appName, boolean remove) {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "getManagersForApp before printcache" + remove);
            printCache();
        }
        ArrayList<WebSecurityManager> wslist = getManagersForApp(SECURITY_MANAGERS, CONTEXT_IDS, appName,
                remove);
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "getManagersForApp after printcache" + remove);
            printCache();
        }
        return wslist;
    }

    public static <T> String[] getContextsForApp(String appName, boolean remove) {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "getContextsForApp before printcache" + remove);
        }
        printCache();
        String[] ctxList = getContextsForApp(CONTEXT_IDS, appName, remove);
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "getContextsForApp after printcache" + remove);
            printCache();
        }
        return ctxList;
    }

    public static <T> void addManagerToApp(String ctxId, String name,
            String appName, WebSecurityManager manager) {
        addManagerToApp(SECURITY_MANAGERS, CONTEXT_IDS, ctxId, name,
                appName, manager);
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "addManagerToApp before printcache" + manager);
            printCache();
        }
    }

    public static WebSecurityManager createManager(WebBundleDescriptor wbd,
            boolean register) {
        String ctxId = WebSecurityManager.getContextID(wbd);

        WebSecurityManager manager = null;
        if (register) {
            manager = getManager(ctxId, null, false);
        }
        if (manager == null || !register) {
            try {
                manager = new WebSecurityManager(wbd);
                if (register) {
                    String appName = wbd.getApplication().getRegistrationName();
                    addManagerToApp(ctxId, null, appName, manager);
                }
            } catch (javax.security.jacc.PolicyContextException e) {
                logger.log(Level.FINE, "[Web-Security] FATAL Exception. Unable to create WebSecurityManager: " + e.getMessage());
                throw new RuntimeException(e);
            }
        }
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "WebSecurityManager called with" + register);
            logger.log(Level.FINE, "WebSecurityManager " + manager + "returned");
        }
        return manager;
    }

    static void printCache() {
        if (logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, "CONTEXT_IDS :" + CONTEXT_IDS.toString());
            logger.log(Level.FINE, "SECURITY_MANAGERS :" + SECURITY_MANAGERS.toString());
        }

    }
}

