/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.queplix.core.utils.cache;

import com.queplix.core.error.GenericSystemException;

import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>Cache Remote/Local objects</p>
 *
 * @author Baranov Andrey [ALB]
 * @version $Revision: 1.2 $ $Date: 2006/04/03 06:47:56 $
 */

public class CacheObjectManager
        implements Serializable {

    // Contains the table of home objects.
    private transient Map __localHomeObjectTable;
    private transient Map __remoteHomeObjectTable;

    /**
     * Gets remote EJB home interface.
     *
     * @param jndiName  JNDI name
     * @param homeClass home class
     * @return remote home object
     */
    public Object getRemoteHome(String jndiName, Class homeClass) {

        Map remoteHomeObjectTable = getRemoteHomeObjectTable();
        String key = homeClass.getName();
        Object home = remoteHomeObjectTable.get(key);
        try {
            if(home == null) {
                synchronized(this) {
                    if((home = remoteHomeObjectTable.get(key)) == null) {
                        Object o = new InitialContext().lookup(jndiName);
                        home = PortableRemoteObject.narrow(o, homeClass);
                        remoteHomeObjectTable.put(key, home);
                    }
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
            throw new GenericSystemException(
                    "Can't get remote home object by the name '" +
                            jndiName + "': " + t.getMessage(), t);
        }

        return home;
    }

    /**
     * Gets EJB reference.
     *
     * @param jndiName  JNDI name
     * @param homeClass home class
     * @return remote object
     */
    public Object getRemoteObject(String jndiName, Class homeClass) {

        Object remote = null;
        String key = homeClass.getName();
        Object home = getRemoteHome(jndiName, homeClass);

        try {
            Method createMethod = homeClass.getMethod("create", new Class[0]);
            remote = createMethod.invoke(home, (Object[]) null);

        } catch (Throwable t) {
            t.printStackTrace();
            throw new GenericSystemException(
                    "Can't get remote object by the name '" +
                            jndiName + "': " + t.getMessage(), t);
        }
        return remote;
    }

    /**
     * Gets EJB local home object.
     *
     * @param jndiName  JNDI name
     * @param homeClass home class
     * @return local home object
     */
    public Object getLocalHome(String jndiName, Class homeClass) {

        Map localHomeObjectTable = getLocalHomeObjectTable();

        String key = homeClass.getName();
        Object home = localHomeObjectTable.get(key);

        try {
            if(home == null) {
                synchronized(this) {
                    if((home = localHomeObjectTable.get(key)) == null) {
                        home = new InitialContext().lookup(jndiName);
                        localHomeObjectTable.put(key, home);
                    }
                }
            }

        } catch (Throwable t) {
            t.printStackTrace();
            throw new GenericSystemException(
                    "Can`t get local home object by the name '" +
                            jndiName + "': " + t.getMessage(), t);
        }
        return home;
    }

    /**
     * Gets EJB local reference.
     *
     * @param jndiName  JNDI name
     * @param homeClass home class
     * @return local object
     */
    public Object getLocalObject(String jndiName, Class homeClass) {

        Object local = null;
        String key = homeClass.getName();
        Object home = getLocalHome(jndiName, homeClass);

        try {
            Method createMethod = homeClass.getMethod("create", new Class[0]);
            local = createMethod.invoke(home, (Object[]) null);

        } catch (Throwable t) {
            t.printStackTrace();
            throw new GenericSystemException(
                    "Can`t get local object by the name '" +
                            jndiName + "': " + t.getMessage(), t);
        }
        return local;
    }

    // ----------------------------------------------------- private methods

    private synchronized Map getLocalHomeObjectTable() {
        if(__localHomeObjectTable == null) {
            __localHomeObjectTable = Collections.synchronizedMap(new HashMap());
        }
        return __localHomeObjectTable;
    }

    private synchronized Map getRemoteHomeObjectTable() {
        if(__remoteHomeObjectTable == null) {
            __remoteHomeObjectTable = Collections.synchronizedMap(
                    new HashMap());
        }
        return __remoteHomeObjectTable;
    }
}