/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * 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
 *
 * Initial developer(s): S.Chassande_________________________.
 * Contributor(s): ______________________________________.
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.lib.ejb21.jorm;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.ow2.jonas.lib.ejb21.JContainer;
import org.ow2.jonas.lib.ejb21.TraceEjb;


import org.objectweb.jorm.lib.JormConfiguratorImpl;
import org.objectweb.jorm.api.JormConfigurator;
import org.objectweb.jorm.api.PException;
import org.objectweb.jorm.api.PMapper;
import org.objectweb.jorm.lib.Mapper;
import org.objectweb.jorm.util.api.Loggable;

import org.objectweb.medor.eval.prefetch.lib.PrefetchCacheImpl;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
 * This class manages Jorm mappers. A mapper can be registered for each data
 * source (connection factory) used in each EJB container.
 * The Mapper manager provides also the JormConfigurator instance used to
 * configure Jorm.
 *
 * @author Sebastien Chassande-Barrioz
 */
public class MapperManager {

    /**
     * The singleton instance of the class
     */
    private static MapperManager singleton = null;

    /**
     * It retrieves the unique instance of MapperManager.
     */
    public static MapperManager getInstance() {
        if (singleton == null) {
            singleton = new MapperManager();
        }
        return singleton;
    }

    /**
     * The Logger used in this class
     */
    private Logger logger = null;

    /**
     * This fields contains the association between a mapper ant its name.
     * key: JContainer instance
     * Value: a map (key = connection factory / value = PMapper instance)
     *
     */
    private HashMap mappers = new HashMap();

    /**
     * permits to configure Jorm and its mappers
     */
    private JormConfigurator jormConfigurator;

    protected MapperManager() {
        logger = TraceEjb.logger;
        jormConfigurator = new JormConfiguratorImpl();
        Properties prop = new Properties();
        prop.put("jorm.generator", "org.objectweb.jorm.generator.lib.JormGenerator");
        prop.put("jorm.mimanager", "org.objectweb.jorm.metainfo.lib.JormManager");
        prop.put("jorm.parser", "org.objectweb.jorm.xml2mi.lib.BasicDomParser");
        prop.put("jorm.verifier", "org.objectweb.jorm.verifier.lib.JormVerifier");
        prop.put("jorm.writer", "org.objectweb.jorm.mi2xml.lib.BasicDomWriter");
        prop.put("jorm.mapper.list", "rdb");
        prop.put("jorm.mapper.mifactory.rdb", "org.objectweb.jorm.mapper.rdb.metainfo.RdbMappingFactory");
        prop.put("jorm.mapper.mopfactory.rdb", "org.objectweb.jorm.mapper.rdb.generator.RdbMOPFactory");
        prop.put("jorm.mapper.gcmapping.rdb", "org.objectweb.jorm.mapper.rdb.genclass.RdbGenClassMapping");
        prop.put("jorm.mapper.schmgr.rdb", "org.objectweb.jorm.mapper.rdb.lib.RdbPMappingStructuresManager");
        jormConfigurator.configure(prop);
        jormConfigurator.setLoggerFactory(TraceEjb.loggerFactory);
        if (logger.isLoggable(BasicLevel.DEBUG)) {
            logger.log(BasicLevel.DEBUG, "JormConfigurator created");
        }
    }

    /**
     * Lookup a jorm mapper for a given container and a given connection factory
     * @param c is the container asking the mapper
     * @param cf is the connection factory (datasource for example) represented
     * by the expected mapper.
     * @return the PMapper instance if it exists, otherwise a null value.
     */
    public PMapper getMapper(JContainer c, Object cf) {
        Map m = (Map) mappers.get(c);
        return (m == null ? null : (Mapper) m.get(cf));
    }

    /**
     * Register and start PMapper for a container and a connection factory if
     * another mapper is not registered with the same container and the same
     * connection factory.
     * @param m the mapper to register (never null)
     * @param c the container using the mapper (never null)
     * @param cf the connection factory represented by the mapper (never null)
     * @return the mapper associated to the given container and the given
     * connection factory. The returned mapper can be different from the mapper
     * instance given in parameter if another similar mapper (same container
     * and same connection factory) is already registered.
     * @throws PException if it is not possible to start the mapper.
     */
    public PMapper addMapper(PMapper m, JContainer c, Object cf) throws PException {
        Map map;
        PMapper pm = null;
        synchronized (mappers) {
            map = (Map) mappers.get(c);
            if (map == null) {
                map = new HashMap();
                mappers.put(c, map);
            }
        }
        pm = (PMapper) map.get(cf);
        if (pm != null) {
            return pm;
        }
        synchronized(map) {
            pm = (PMapper) map.get(cf);
            if (pm == null) {
                pm = m;
                //Allocate a new Cache of PreftechBuffer
                m.setPrefetchCache(new PrefetchCacheImpl(logger));
                //Add a logger
                if (TraceEjb.loggerFactory == null) {
                    m.setLogger(logger);
                } else {
                    m.setLogger(TraceEjb.loggerFactory.getLogger(TraceEjb.prefix
                            + ".mapper." + m.getMapperName()));
                    if (m instanceof Loggable) {
                        ((Loggable) m).setLoggerFactory(TraceEjb.loggerFactory);
                    }
                }
                // start the mapper
                m.start();
                //register mapper
                map.put(cf, m);
                if (logger.isLoggable(BasicLevel.DEBUG)) {
                    logger.log(BasicLevel.DEBUG, "Mapper (" + m.getMapperName()
                            + ", " + cf + ") initialized");
                }
            }
        }
        return pm;
    }

    public Logger getLogger() {
        return logger;
    }

    public JormConfigurator getJormConfigurator() {
        return jormConfigurator;
    }
}
