/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2007 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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.security.internal.realm.factory;

import java.io.Serializable;
import java.util.Hashtable;

import javax.management.MalformedObjectNameException;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Referenceable;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.lib.reconfig.ReconfigEmitter;
import org.ow2.jonas.lib.reconfig.XMLConfigurationData;
import org.ow2.jonas.lib.util.JonasObjectName;
import org.ow2.jonas.lib.util.Log;
import org.ow2.jonas.security.SecurityService;
import org.ow2.jonas.security.internal.realm.principal.Group;
import org.ow2.jonas.security.internal.realm.principal.Role;
import org.ow2.jonas.security.internal.realm.principal.User;
import org.ow2.jonas.security.realm.factory.JResource;
import org.ow2.jonas.security.realm.principal.JUser;

/**
 * This class implements JOnAS realm factory objects. It is build from an xml
 * file by the security service of JOnAS.
 * @author Florent Benoit
 */
public abstract class AbstractJResource extends ReconfigEmitter implements Serializable, Referenceable, JResourceMBean, JResource {

    /**
     * Name of this resource (realm name).
     */
    private String name = null;

    /**
     * The logger used in JOnAS.
     */
    private static Logger logger = Log.getLogger(Log.JONAS_SECURITY_PREFIX);

    /**
     * Hashtable used to cache the users (speed up search).
     */
    private Hashtable<String, JUser> users = null;

    /**
     * Value used as sequence number by reconfiguration notifications.
     */
    private long sequenceNumber;

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

    /**
     * Reference to the jmx service.
     */
    private JmxService jmxService = null;

    /**
     * name of the management domain.
     */
    private String domainName = null;

    /**
     * Constructor.
     * @throws Exception if a service can't be retrieved
     */
    public AbstractJResource() throws Exception {
        users = new Hashtable<String, JUser>();
        sequenceNumber = 0;
    }

    public void setDomainName(final String domain) {
        domainName = domain;
    }

    public void setJmxService(final JmxService jmx) {
        jmxService = jmx;
    }

    public void setSecurityService(final SecurityService sec) {
        securityService = sec;
    }

    /**
     * @param name the resource (realm) name
     */
    public void setName(final String name) {
        this.name = name;
    }

    /**
     * @return the resource (realm) name
     */
    public String getName() {
        return name;
    }

    /**
     * @return the users table
     */
    public Hashtable<String, JUser> getUsers() {
        return users;
    }

    /**
     * Return a sequence number and increase this number.
     * @return a sequence number
     */
    protected long getSequenceNumber() {
        return ++sequenceNumber;
    }

    /**
     * Save the resource configuration.
     */
    public void saveConfig() {

        if (securityService != null) {
            // Retrieve the xml value by the Security service
            String xml = securityService.toXML();
            XMLConfigurationData data = new XMLConfigurationData(xml);

            // Send a reconfiguration notification to the listener MBean
            sendReconfigNotification(getSequenceNumber(), name, data);
            sendSaveNotification(getSequenceNumber(), name);

            // rebind this resource into the jndi
            try {
                Context ictx = new InitialContext();
                ictx.rebind(getName(), this);
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "jResource " + getName() + " is rebind into the registry.");
                }
            } catch (NamingException e) {
                logger.log(BasicLevel.ERROR, "Cannot rebind factory to the jndi : '" + e.getMessage() + "'");
            }

        }
    }

    /**
     * @return Returns the logger.
     */
    public static Logger getLogger() {
        return logger;
    }

    /**
     * @param logger The logger to set.
     */
    public static void setLogger(final Logger logger) {
        AbstractJResource.logger = logger;
    }

    /**
     * @param users the users table
     */
    public void setUsers(final Hashtable<String, JUser> users) {
        this.users = users;
    }

    /**
     *
     */
    public void clearCache() {
        setUsers(new Hashtable<String, JUser>());
    }

    /**
     * Allows for listener objects to receive notifications emitted by
     * this resource.
     * @param arg0 listener object
     * @param arg1 notification filter
     * @param arg2 handback object
     */
    @Override
    public void addNotificationListener(final NotificationListener arg0,
                                        final NotificationFilter arg1,
                                        final Object arg2) {
        super.addNotificationListener(arg0, arg1, arg2);
    }

    /**
     * Create and register an MBean for the user.
     * @param user the user to be managed
     * @throws MalformedObjectNameException if we could not construct ObjectName for the user
     */
    protected void registerUserMBean(final User user) throws MalformedObjectNameException {
        if (jmxService != null) {
            String userName = user.getName();
            ObjectName on = JonasObjectName.user(domainName, getName(), userName);
            jmxService.registerMBean(user, on);
        }
    }

    /**
     * Unregister MBean associated to a user.
     * @param resourceName
     * @param userName
     * @throws MalformedObjectNameException if MBean is not unregistered
     */
    protected void unregisterUserMBean(final String resourceName, final String userName) throws MalformedObjectNameException {
        if (jmxService != null) {
            jmxService.unregisterMBean(JonasObjectName.user(domainName, resourceName, userName));
        }
    }

    /**
     * Create and register an MBean for the group.
     * @param group the group to be managed
     * @throws MalformedObjectNameException if MBean is not created
     */
    protected void registerGroupMBean(final Group group) throws MalformedObjectNameException {
        if (jmxService != null) {
            String groupName = group.getName();
            ObjectName on = JonasObjectName.group(domainName, getName(), groupName);
            jmxService.registerMBean(group, on);
        }
    }

    /**
     * Unregister MBean associated to a group.
     * @param resourceName
     * @param groupName
     * @throws MalformedObjectNameException could not construct ObjectName for the user
     */
    protected void unregisterGroupMBean(final String resourceName, final String groupName) throws MalformedObjectNameException {
        if (jmxService != null) {
            jmxService.unregisterMBean(JonasObjectName.group(domainName, resourceName, groupName));
        }
    }

    /**
     * Create and register an MBean for this role.
     * @param role the role to be managed
     * @throws MalformedObjectNameException not construct ObjectName for this role
     */
    protected void registerRoleMBean(final Role role) throws MalformedObjectNameException {
        if (jmxService != null) {
            String roleName = role.getName();
            ObjectName on = JonasObjectName.role(domainName, getName(), roleName);
            jmxService.registerMBean(role, on);
        }
    }

    /**
     *
     * @param resourceName
     * @param roleName
     * @throws MalformedObjectNameException could not construct ObjectName for this role
     */
    protected void unregisterRoleMBean(final String resourceName, final String roleName) throws MalformedObjectNameException {
       if (jmxService != null) {
            jmxService.unregisterMBean(JonasObjectName.role(domainName, resourceName, roleName));
        }
    }

}
