/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2007-2010 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id: Realm.java 20987 2011-03-14 12:10:10Z benoitf $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.web.tomcat7.security;

import java.io.IOException;
import java.security.Principal;
import java.security.acl.Group;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.security.auth.Subject;
import javax.security.auth.login.AccountExpiredException;
import javax.security.auth.login.CredentialExpiredException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.deploy.LoginConfig;
import org.apache.catalina.deploy.SecurityConstraint;
import org.apache.catalina.realm.Constants;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.catalina.realm.RealmBase;
import org.apache.tomcat.util.res.StringManager;
import org.ow2.jonas.deployment.web.ServletDesc;
import org.ow2.jonas.deployment.web.WebContainerDeploymentDesc;
import org.ow2.jonas.lib.security.auth.JSigned;
import org.ow2.jonas.lib.security.context.SecurityContext;
import org.ow2.jonas.lib.security.context.SecurityCurrent;
import org.ow2.jonas.security.SecurityService;
import org.ow2.jonas.security.auth.callback.NoInputCallbackHandler;
import org.ow2.jonas.security.realm.factory.JResource;
import org.ow2.jonas.security.realm.factory.JResourceException;
import org.ow2.jonas.security.realm.principal.JUser;
import org.ow2.jonas.web.base.lib.PermissionManager;
import org.ow2.jonas.web.tomcat7.JOnASStandardContext;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * <p>
 * Implementation of a Realm. (by a wrapper)<br>
 * Use any JOnAS realm by specifying the resource name This implementation
 * manages the security with JACC specification It implements Cloneable to allow
 * clones. Each context must have its own Realm. A realm can not be shared
 * across different contexts or an engine. This is because each realm is
 * associated to a permission manager Extends the Realmbase class of the Tomcat
 * Server.<br>
 * This Realm can use also the LoginModule for the authentication
 * @author Florent Benoit
 */
public class Realm extends RealmBase implements Cloneable {

    /**
     * Descriptive information about this Realm implementation.
     */
    private static final String NAME = Realm.class.getSimpleName();

    /**
     * Descriptive information about this Realm implementation.
     */
    private static final String INFO =  Realm.class.getName() + "/1.0";

    /**
     * Name used in the JAAS config file.
     */
    private static final String DEFAULT_JAAS_ENTRY_NAME = "tomcat";

    /**
     * The string manager for this package.
     */
    private static StringManager sm = StringManager.getManager(Constants.Package);

    /**
     * The logger used in JOnAS.
     */
    private static Log logger = LogFactory.getLog(Realm.class);

    /**
     * The resource we will use to authenticate users and identify associated
     * roles.
     */
    private JResource jResource = null;

    /**
     * The name of the resource.
     */
    private String resourceName = null;

    /**
     * The entry name of the JAAS resource.
     */
    private String jaasEntry = DEFAULT_JAAS_ENTRY_NAME;

    /**
     * Reference to the JOnAS security service.
     */
    private SecurityService securityService = null;

    /**
     * Permission manager used by this realm for JACC permissions.
     */
    private PermissionManager permissionManager = null;

    /**
     * Last request that has been send to hasUserDataPermission or
     * hasResourcePermission methods Used in hasRole to know the current servlet
     * name. This request is store in a local thread.
     */
    private ThreadLocal<Request> lastRequestThread = new ThreadLocal<Request>();

    /**
     * Context (used to retrieve web.xml informations).
     */
    private Context context = null;

    /**
     * Name of this Realm for traces.
     */
    private String realmName = NAME;

    /**
     * Return descriptive information about this Realm implementation and the
     * corresponding version number, in the format.
     * <code>&lt;description&gt;/&lt;version&gt;</code>.
     * @return the info.
     */
    @Override
    public String getInfo() {
        return INFO;
    }

    /**
     * Return the resource name we will be using.
     * @return the resource name.
     */
    public String getResourceName() {
        return resourceName;
    }

    /**
     * Set the resource name we will be using.
     * @param resourceName The new resource name
     */
    public void setResourceName(final String resourceName) {
        this.resourceName = resourceName;

    }

    /**
     * Return the JAAS entry name we will be using.
     * @return the JAAS entry name.
     */
    public String getJaasEntry() {
        return jaasEntry;
    }

    /**
     * Set the JAAS entry we will be using.
     * @param jaasEntry the JAAS entry name
     */
    public void setJaasEntry(final String jaasEntry) {
        this.jaasEntry = jaasEntry;
    }

    /**
     * Set the permission manager used by this realm.
     * @param permissionManager the permission manager to use
     */
    public void setPermissionManager(final PermissionManager permissionManager) {
        this.permissionManager = permissionManager;

    }

    /**
     * Return the SecurityConstraints configured to guard the request URI for
     * this request, or <code>null</code> if there is no such constraint.
     * @param request Request we are processing
     * @param context Context the Request is mapped to
     * @return security constraints configured to guard the request URI
     */
    @Override
    public SecurityConstraint[] findSecurityConstraints(final Request request, final Context context) {
        // Use super Method
        return super.findSecurityConstraints(request, context);
    }

    /**
     * Perform access control based on the specified authorization constraint.
     * @return <code>true</code> if this constraint is satisfied and
     *         processing should continue, or <code>false</code> otherwise.
     * @param request Request we are processing
     * @param response Response we are creating
     * @param constraints Security constraint we are enforcing
     * @param context The Context to which client of this class is attached.
     * @exception IOException if an input/output error occurs
     */
    @Override
    public boolean hasResourcePermission(final Request request, final Response response, final SecurityConstraint[] constraints,
            final Context context) throws IOException {

        // Update request
        lastRequestThread.set(request);

        // --- Use code of RealmBase for the Login / Error pages

        // Specifically allow access to the form login and form error pages
        // and the "j_security_check" action
        LoginConfig config = context.getLoginConfig();
        if (config != null && Constants.FORM_METHOD.equals(config.getAuthMethod())) {
            String requestURI = request.getDecodedRequestURI();
            String loginPage = context.getPath() + config.getLoginPage();
            if (loginPage.equals(requestURI)) {
                logger.debug("{0}: Allow access to login page {1}", realmName, loginPage);
                return true;
            }

            String errorPage = context.getPath() + config.getErrorPage();
            if (errorPage.equals(requestURI)) {
                logger.debug("{0}: Allow access to error page {1}", realmName, errorPage);
                return true;
            }
            if (requestURI.endsWith(Constants.FORM_ACTION)) {
                logger.debug("{0}: Allow access to username/password submission", realmName);
                return true;
            }
        }

        // Which user principal have we already authenticated?
        Principal principal = request.getUserPrincipal();

        // --- End of code from RealmBase class

        String[] roles = null;
        String principalName = null;
        if (principal instanceof GenericPrincipal) {
            roles = ((GenericPrincipal) principal).getRoles();
            principalName = principal.getName();
        }

        if (permissionManager == null) {
            logger.error("{0}: No permission manager is set. Realm used without using the JOnAS deployer but only Tomcat.",
                    realmName);
            return false;
        }

        boolean hasResourcePermission = permissionManager.checkWebResourcePermission(request, principalName, roles);

        // Need to send HTTP status code as invoke() method of Authenticator
        // expect that it is
        // done by the realm.
        if (!hasResourcePermission) {
            // Return a "Forbidden" message denying access to this resource
            response.sendError(HttpServletResponse.SC_FORBIDDEN, sm.getString("realmBase.forbidden"));
        }

        return hasResourcePermission;
    }

    /**
     * Return <code>true</code> if the specified Principal has the specified
     * security role, within the context of this Realm; otherwise return
     * <code>false</code>.  This method can be overridden by Realm
     * implementations, but the default is adequate when an instance of
     * <code>GenericPrincipal</code> is used to represent authenticated
     * Principals from this Realm.
     *
     * @param principal Principal for whom the role is to be checked
     * @param role Security role to be checked
     */
    @Override
    public boolean hasRole(Wrapper wrapper, Principal principal, String role) {

        // Check for a role alias defined in a <security-role-ref> element
        if (wrapper != null) {
            String realRole = wrapper.findSecurityReference(role);
            if (realRole != null)
                role = realRole;
        }


        if ((principal == null) || (role == null) || !(principal instanceof GenericPrincipal)) {
            return false;
        }

        if (logger.isDebugEnabled()) {
            logger.debug("{0}: Principal = {1}", realmName, principal);
            logger.debug("{0}: Role = {1}", realmName, role);
        }

        if (context == null) {
            logger.error("{0}: Cannot find a servlet name for isUserInRole() as no context was found", realmName);
            return false;
        }

        Request req = lastRequestThread.get();

        if (req == null) {
            logger.error("{0}: Cannot find a servlet name for isUserInRole(). No previous request !", realmName);
            return false;
        }
        String servletName = findServletName(req);
        String[] roles = null;
        String principalName = null;

        if (principal instanceof GenericPrincipal) {
            roles = ((GenericPrincipal) principal).getRoles();
            principalName = principal.getName();
        }

        if (permissionManager == null) {
            logger.error(
                    "{0}: No permission manager is set. Using this realm without using the JOnAS deployer but only Tomcat.",
                    realmName);
            return false;
        }

        boolean hasRole = permissionManager.checkWebRoleRefPermission(req, servletName, principalName, roles, role);
        return hasRole;
    }

    /**
     * @return the name of the servlet (or "" if it's a JSP for example) This
     *         servlet name is used to build JACC permission for the hasRole
     *         method
     * @param request the servlet request with which we have to find servlet
     *        name
     */
    private String findServletName(final Request request) {

        // Servlet name cannot be found
        if (request == null || request.getRequestURI() == null || request.getContextPath() == null) {
            return null;
        }

        // Pattern of the user (remove path)
        String userPattern = request.getRequestURI().substring(request.getContextPath().length());

        if (logger.isDebugEnabled()) {
            logger.debug("{0}: User Pattern = {1}", realmName, userPattern);
        }

        String servletName = "";
        String[] patterns = context.findServletMappings();
        boolean foundServlet = false;
        String pattern = "";
        int i = 0;

        // Try to search servlet name
        while ((i < patterns.length) && !foundServlet) {
            pattern = patterns[i];
            if (logger.isDebugEnabled()) {
                logger.debug("{0}: Pattern found = {1}", realmName, pattern);
                logger.debug("{0}: Servlet name for pattern = {1}", realmName, context.findServletMapping(pattern));
            }

            // Extension pattern and ends with extension
            if (pattern.startsWith("*.") && userPattern.endsWith(pattern.substring(1))) {
                foundServlet = true;
                continue;
            }

            // Exact Servlet name (ie pattern = /ServletName and userPattern =
            // /ServletName)
            if (pattern.equals(userPattern)) {
                foundServlet = true;
                continue;
            }

            i++;
        }

        if (foundServlet) {
            servletName = context.findServletMapping(pattern);
            // JSP case. servlet name must be empty as required by JACC
            // specification
            if (servletName.equals("jsp")) {
                servletName = "";
            }
            if (logger.isDebugEnabled()) {
                logger.debug("{0}: Found servlet name = {1}", realmName, servletName);
            }
        }
        return servletName;
    }

    /**
     * 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.
     * @param request Request we are processing
     * @param response Response we are creating
     * @param constraints Security constraints being checked
     * @exception IOException if an input/output error occurs
     */
    @Override
    public boolean hasUserDataPermission(final Request request, final Response response, final SecurityConstraint[] constraints)
            throws IOException {

        // Update request
        lastRequestThread.set(request);

        // ---- Start of copy from RealmBase class ---

        // Validate the request against the user data constraint
        if (request.getRequest().isSecure()) {
            if (logger.isDebugEnabled()) {
                logger.debug("{0}: User data constraint already satisfied", realmName);
            }
            return true;
        }

        // Which user principal have we already authenticated?
        Principal principal = ((HttpServletRequest) request).getUserPrincipal();

        // ---- End of copy from RealmBase class ---

        String[] roles = null;
        String principalName = null;
        if (principal instanceof GenericPrincipal) {
            roles = ((GenericPrincipal) principal).getRoles();
            principalName = principal.getName();
        }

        // ---- Start of copy from RealmBase class ---
        for (int i = 0; i < constraints.length; i++) {
            SecurityConstraint constraint = constraints[i];
            // Use redirect only if it the transport protocol is integral or
            // confidential
            String userConstraint = constraint.getUserConstraint();

            // Redirect only if the constraint is INTEGRAL or CONFIDENTIAL !
            if (userConstraint != null
                    && (userConstraint.equals(Constants.INTEGRAL_TRANSPORT) || userConstraint
                            .equals(Constants.CONFIDENTIAL_TRANSPORT))) {
                // Initialize variables we need to determine the appropriate
                // action
                int redirectPort = request.getConnector().getRedirectPort();

                // Is redirecting disabled?
                if (redirectPort <= 0) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("{0}: SSL redirect is disabled", realmName);
                    }
                    response.sendError(HttpServletResponse.SC_FORBIDDEN, request.getRequestURI());
                    return false;
                }

                // Redirect to the corresponding SSL port
                StringBuffer file = new StringBuffer();
                String protocol = "https";
                String host = request.getServerName();
                // Protocol
                file.append(protocol).append("://");
                // Host with port
                file.append(host).append(":").append(redirectPort);
                // URI
                file.append(request.getRequestURI());
                String requestedSessionId = request.getRequestedSessionId();
                if ((requestedSessionId != null) && request.isRequestedSessionIdFromURL()) {
                    file.append(";jsessionid=");
                    file.append(requestedSessionId);
                }
                String queryString = request.getQueryString();
                if (queryString != null) {
                    file.append('?');
                    file.append(queryString);
                }

                if (logger.isDebugEnabled()) {
                    logger.debug("{0}: Redirecting to {1}", realmName, file);
                }
                response.sendRedirect(file.toString());

                return false;
            }
        }

        // ---- End of copy from RealmBase ---
        if (permissionManager == null) {
            logger.error("{0}: No permission manager is set. Realm used without using the JOnAS deployer but only Tomcat.",
                    realmName);
            return false;
        }

        // If Transport protocol is NONE :
        boolean hasUserDataPermission = permissionManager.checkWebUserDataPermission(request, principalName, roles);
        return hasUserDataPermission;

    }

    /**
     * Return the Principal associated with the specified username and
     * credentials, if there is one; otherwise return <code>null</code>.
     * @param username Username of the Principal to look up
     * @param credentials Password or other credentials to use in authenticating
     *        this username
     * @return the principal associated
     */
    @Override
    public Principal authenticate(final String username, final String credentials) {
        // Use JOnAS resource if present
        if (jResource != null) {
            return authenticateResource(username, credentials);
        } else {
            // else use JAAS mechanism
            return authenticateJAAS(username, credentials);
        }
    }

    /**
     * Return the Principal associated with the specified username and
     * credentials, if there is one; otherwise return <code>null</code>.
     * @param username Username of the Principal to look up
     * @param credentials Password or other credentials to use in authenticating
     *        this username
     * @return the principal associated
     */
    public Principal authenticateResource(final String username, final String credentials) {

        // No authentication can be made with a null username
        if (username == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("{0}: No username so no authentication", realmName);
            }
            return null;
        }

        // Does a user with this username exist?
        JUser user = null;
        try {
            user = jResource.findUser(username);
        } catch (Exception jre) {
            // could not retrieve user
            logger.error("{0}: Cannot find the user {1}", realmName, username, jre);
            return null;
        }

        // User was not found
        if (user == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("{0}: User {1} not found.", realmName, username);
            }
            return null;
        }

        boolean validated = jResource.isValidUser(user, credentials);
        if (!validated) {
            logger.error("{0}: The password for the user {1} is not valid", realmName, username);
            return null;
        }

        List<String> combinedRoles = null;
        try {
            combinedRoles = jResource.getArrayListCombinedRoles(user);
        } catch (JResourceException jre) {
            logger.error("{0}: Cannot get the roles from the user {1}", realmName, username, jre);
            return null;
        }

        GenericPrincipal principal = new GenericPrincipal(user.getName(), user.getPassword(), combinedRoles);
        SecurityContext ctx = new SecurityContext(principal.getName(), combinedRoles);

        // Get the servlet name
        Request request = lastRequestThread.get();
        String servletName = findServletName(request);

        if (servletName != null) {

            // Use the web deployment descriptor to get the role name of the runas element of the servlet
            WebContainerDeploymentDesc webDD = ((JOnASStandardContext) context).getWebDeploymentDescriptor();

            Collection<ServletDesc> servletDescList = webDD.getServletDescList();
            for (Iterator iterator = servletDescList.iterator(); iterator.hasNext();) {
                ServletDesc servletDesc = (ServletDesc) iterator.next();

                // Servlet declaration found
                if (servletDesc.getServletName().equals(servletName)) {
                    if (servletDesc.getServletRunAS() != null) {
                        String role = servletDesc.getServletRunAS().getRoleName();
                        String principalName = webDD.getServletPrincipalName(servletName);

                        if (principalName == null) {
                            principalName = role;
                        }

                        ctx.pushRunAs(role, principalName, new String[] {role});
                    }

                    // Exit the loop
                    break;
                }
            }
        }

        SecurityCurrent current = SecurityCurrent.getCurrent();
        current.setSecurityContext(ctx);

        return principal;
    }

    /**
     * Return the Principal associated with the specified username and
     * credentials, if there is one; otherwise return <code>null</code>.
     * @param username Username of the Principal to look up
     * @param credentials Password or other credentials to use in authenticating
     *        this username
     * @return the principal associated
     */
    public Principal authenticateJAAS(final String username, final String credentials) {

        // No authentication can be made with a null username
        if (username == null) {
            logger.error("{0}: No username so no authentication", realmName);
            return null;
        }
        // Establish a LoginContext to use for authentication
        LoginContext loginContext = null;
        try {
            loginContext = new LoginContext(jaasEntry, new NoInputCallbackHandler(username, credentials));
        } catch (LoginException e) {
            logger.error("{0}: LoginException for user {1}", realmName, username, e);
            return null;
        }
        // Negotiate a login via this LoginContext
        Subject subject = null;
        try {
            loginContext.login();
            subject = loginContext.getSubject();
            if (subject == null) {
                logger.error("{0}: No subject for the user {1}", realmName, username);
                return null;
            }
        } catch (AccountExpiredException e) {
            logger.error("{0}: Account expired for the user {1}", realmName, username, e);
            return null;
        } catch (CredentialExpiredException e) {
            logger.error("{0}: Credential expired for the user {1}", realmName, username, e);
            return null;
        } catch (FailedLoginException e) {
            logger.error("{0}: Failed Login for the user {1}", realmName, username, e);
            return null;
        } catch (LoginException e) {
            logger.error("{0}: Login exception for the user {1}", realmName, username, e);
            return null;
        }

        // Get credentials iterators from the subject
        Iterator<Object> credentialsIterator = subject.getPrivateCredentials().iterator();
        String credential = (String) credentialsIterator.next();

        // Retrieve first principal name found (without groups)
        Iterator<Principal> iterator = subject.getPrincipals(Principal.class).iterator();
        String userName = null;
        while (iterator.hasNext() && (userName == null)) {
            Principal principal = iterator.next();
            if (!(principal instanceof Group)) {
                userName = principal.getName();
            }
        }

        // No name --> error
        if (userName == null) {
            logger.error("{0}: No Username found in the subject", realmName);
            return null;
        }

        // Signed object (if any)
        JSigned jSigned = null;

        // Retrieve all roles of the user (Roles are members of the Group.class)
        Set<Group> groups = subject.getPrincipals(Group.class);
        List<String> roles = new ArrayList<String>();
        for (Group group : groups) {

            // Signed group (empty group that contains a signature)?
            if (group instanceof JSigned) {
                jSigned = (JSigned) group;
                continue;
            }

            Enumeration<? extends Principal> e = group.members();
            while (e.hasMoreElements()) {
                Principal p = e.nextElement();
                roles.add(p.getName());
            }
        }

        GenericPrincipal principal = new GenericPrincipal(userName, credential, roles);
        // instanciation of the security context
        SecurityContext ctx = new SecurityContext(userName, roles);
        // Signed ?
        if (jSigned != null) {
            ctx.setSignature(jSigned.getSignature());
        }
        SecurityCurrent current = SecurityCurrent.getCurrent();
        current.setSecurityContext(ctx);

        return principal;
    }

    /**
     * Return the Principal associated with the specified chain of X509 client
     * certificates. If there is none, return <code>null</code>.
     * @param cert Array of client certificates, with the first one in the array
     *        being the certificate of the client itself.
     * @return the associated Principal
     */
    @Override
    public Principal authenticate(final X509Certificate[] cert) {
        String dn = cert[0].getSubjectDN().getName();
        return authenticate(dn, "tomcat");
    }

    /**
     * Return a short name for this Realm implementation.
     * @return the name
     */
    @Override
    protected String getName() {
        return NAME;
    }

    /**
     * Return the password associated with the given principal's user name.
     * @param username the given principal's user name.
     * @return the password associated.
     */
    @Override
    protected String getPassword(final String username) {
        return null;
    }

    /**
     * Return the Principal associated with the given user name.
     * @param username the given principal's user name.
     * @return the Principal associated
     */
    @Override
    protected Principal getPrincipal(final String username) {
        return null;
    }

    /**
     * Set the context of this Realm. This is used to retrieve xml information
     * of the web.xml file
     * @param context Context for this realm
     */
    public void setContext(final Context context) {
        this.context = context;
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        sb.append(NAME);
        sb.append(":");
        sb.append(resourceName);
        sb.append(":");
        if (context != null) {
            sb.append(context.getName());
        }
        sb.append("] ");
        this.realmName = sb.toString();
    }

    /**
     * Assign the {@link SecurityService} instance to be used for authentication.
     * @param securityService {@link SecurityService} to use.
     */
    public void setSecurityService(final SecurityService securityService) {
        this.securityService = securityService;
    }

    /**
     * Prepare for active use of the public methods of this Component.
     * @exception LifecycleException if this component detects a fatal error
     *            that prevents it from being started
     */
    @Override
    public synchronized void startInternal() throws LifecycleException {

        // Get the resource from the security service if working with resource
        // mode
        if (resourceName != null) {
            if (securityService == null) {
                throw new LifecycleException("Security service not set, cannot start");
            }
            jResource = securityService.getJResource(resourceName);
            if (jResource == null) {
                throw new LifecycleException("Can't retrieve resource '" + resourceName + "' from the security service");
            }

            // This is a configuration error to set bot resourceName (for JACC) and jaasEntry (for JAAS)
            if (!DEFAULT_JAAS_ENTRY_NAME.equals(jaasEntry)) {
                throw new LifecycleException("Invalid Realm configuration: cannot use both resourceName and jaasEntry attributes.");
            }
        }

        // Perform normal superclass initialization
        super.startInternal();

    }

    /**
     * Gracefully shut down active use of the public methods of this Component.
     * @exception LifecycleException if this component detects a fatal error
     *            that needs to be reported
     */
    @Override
    public synchronized void stopInternal() throws LifecycleException {
        // Perform normal superclass finalization
        super.stopInternal();

        // Release reference to our resource
        jResource = null;
    }

    /**
     * Creates and returns a copy of this object.
     * @return copy of this object.
     * @throws CloneNotSupportedException if the copy fails
     */
    @Override
    public Object clone() throws CloneNotSupportedException {
        Realm jRealm = new Realm();
        jRealm.setResourceName(resourceName);
        jRealm.setJaasEntry(jaasEntry);
        jRealm.setSecurityService(securityService);
        return jRealm;
    }

    /**
     * @return the permission manager used by this realm.
     */
    public PermissionManager getPermissionManager() {
        return permissionManager;
    }

}
