/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2008 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * Application Versioning System
 * Copyright (C) 2008 France Telecom R&D
 *
 * 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 1any 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$
 * --------------------------------------------------------------------------
 */

// Use the org.ow2.carol.jndi.spi instead of an org.ow2.jonas.* package in
// order to make the transition between MultiContext and VirtualJNDILookup
// transparent for CAROL bundle inclusions.
package org.ow2.carol.jndi.spi;

import java.util.Hashtable;

import javax.naming.Name;
import javax.naming.NamingException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ow2.jonas.versioning.VersioningService;

/**
 * Handles virtual JNDI lookup.
 *
 * @author S. Ali Tokmen
 */
public class VirtualJNDILookup extends MultiContext {

    /**
     * Logger.
     */
    private static Log logger = LogFactory.getLog(VirtualJNDILookup.class);

    /**
     * The environment of the InitialContext.
     */
    private Hashtable<?, ?> environment;

    /**
     * Versioning service.
     */
    private static VersioningService versioningService;

    /**
     * Calls {@link MultiContext#MultiContext(Hashtable)}(env).
     *
     * @param env The environment of the InitialContext.
     *
     * @throws NamingException If cccf.getNewContextHashtable(env) fails.
     */
    public VirtualJNDILookup(final Hashtable<?, ?> env) throws NamingException {
        super(env);
        environment = env;
    }

    /**
     * Retrieves the named object using the prefixed naming strategy:
     *      - First, the name prefix + name is looked up,
     *      - If not found, name alone is looked up.
     * Note that name is looked up directly if no prefix exists.
     *
     * @param name Name of the object to look up
     *
     * @return The object bound to <tt>name</tt>
     *
     * @throws NamingException If a naming exception is encountered
     */
    @Override
    public Object lookup(final String name) throws NamingException {
        // First, try to match the full (prefixed) name
        String prefix = getPrefix();
        if (prefix != null) {
            Object result;
            try {
                result = super.lookup(prefix + name);
            } catch(NamingException ignored) {
                result = null;
            }
            if (result != null) {
                return result;
            } else {
                logger.info("No object found for the JNDI name '" + name + "' and prefix '" + prefix
                        + "'. Now looking up for non-prefixed version of the same name.");
            }
        }

        // Prefixed name not found, look for the "normal" name
        // This will return the JNDI name bound as "default"
        return super.lookup(name);
    }

    /**
     * Retrieves the named object using the prefixed naming strategy:
     *      - First, the name prefix + name is looked up,
     *      - If not found, name alone is looked up.
     * Note that name is looked up directly if no prefix exists.
     *
     * @param name Name of the object to look up
     *
     * @return The object bound to <tt>name</tt>
     *
     * @throws NamingException If a naming exception is encountered
     */
    @Override
    public Object lookup(final Name name) throws NamingException {
        // First, try to match the full (prefixed) name
        String prefix = getPrefix();
        if (prefix != null) {
            Object result;
            try {
                result = super.lookup(prefix + name);
            } catch(NamingException ignored) {
                result = null;
            }
            if (result != null) {
                return result;
            } else {
                logger.info("No object found for the JNDI name '" + name + "' and prefix '" + prefix
                        + "'. Now looking up for non-prefixed version of the same name.");
            }
        }

        // Prefixed name not found, look for the "normal" name
        // This will return the JNDI name bound as "default"
        return super.lookup(name);
    }

    /**
     * Retrieves the named object, following links except for the terminal
     * atomic component of the name, using the prefixed naming strategy:
     *      - First, the name prefix + name is looked up,
     *      - If not found, name alone is looked up.
     * Note that name is looked up directly if no prefix exists.
     *
     * @param name Name of the object to look up
     *
     * @return The object bound to <tt>name</tt>, not following the terminal
     *         link (if any)
     *
     * @throws NamingException if a naming exception is encountered
     */
    @Override
    public Object lookupLink(final String name) throws NamingException {
        // First, try to match the full (prefixed) name
        String prefix = getPrefix();
        if (prefix != null) {
            Object result;
            try {
                result = super.lookupLink(prefix + name);
            } catch(NamingException ignored) {
                result = null;
            }
            if (result != null) {
                return result;
            } else {
                logger.info("No object found for the JNDI name '" + name + "' and prefix '" + prefix
                        + "'. Now looking up for non-prefixed version of the same name.");
            }
        }

        // Prefixed name not found, look for the "normal" name
        // This will return the JNDI name bound as "default"
        return super.lookupLink(name);
    }

    /**
     * Retrieves the named object, following links except for the terminal
     * atomic component of the name, using the prefixed naming strategy:
     *      - First, the name prefix + name is looked up,
     *      - If not found, name alone is looked up.
     * Note that name is looked up directly if no prefix exists.
     *
     * @param name Name of the object to look up
     *
     * @return The object bound to <tt>name</tt>, not following the terminal
     *         link (if any)
     *
     * @throws NamingException if a naming exception is encountered
     */
    @Override
    public Object lookupLink(final Name name) throws NamingException {
        // First, try to match the full (prefixed) name
        String prefix = getPrefix();
        if (prefix != null) {
            Object result;
            try {
                result = super.lookupLink(prefix + name);
            } catch(NamingException ignored) {
                result = null;
            }
            if (result != null) {
                return result;
            } else {
                logger.info("No object found for the JNDI name '" + name + "' and prefix '" + prefix
                        + "'. Now looking up for non-prefixed version of the same name.");
            }
        }

        // Prefixed name not found, look for the "normal" name
        // This will return the JNDI name bound as "default"
        return super.lookupLink(name);
    }

    /**
     * Gets the JNDI naming prefix. This can be set using the EAR or the via
     * the InitialContext properties.
     *
     * @return JNDI naming prefix to use, null if none found.
     */
    protected String getPrefix() {
        String prefix;
        try {
            prefix = (String) new MultiContext(environment).lookup("java:comp/env/JNDILookupPrefix");
        } catch(NamingException e) {
            prefix = null;
        }

        if (prefix != null) {
            return prefix;
        } else {
            // TODO: get prefix from environment
            return null;
        }
    }

    /**
     * @param versioningService  The versioning service to set.
     */
    public static void setVersioningService(final VersioningService versioningService) {
        VirtualJNDILookup.versioningService = versioningService;
    }

    /**
     * Sets the versioning service to null.
     */
    public static void unsetVersioningService() {
        VirtualJNDILookup.versioningService = null;
    }

    /**
     * @return The versioning service.
     */
    public static VersioningService getVersioningService() {
        return VirtualJNDILookup.versioningService;
    }
}
