Package org.ow2.easybeans.component.smartclient.spi

Source Code of org.ow2.easybeans.component.smartclient.spi.SmartContextFactory

/**
* EasyBeans
* Copyright (C) 2006 Bull S.A.S.
* Contact: easybeans@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: SmartContextFactory.java 5369 2010-02-24 14:58:19Z benoitf $
* --------------------------------------------------------------------------
*/

package org.ow2.easybeans.component.smartclient.spi;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;

import org.ow2.easybeans.component.smartclient.client.AskingClassLoader;

/**
* Initial Context factory used on the client side.<br>
* It will ask the server for every class/resource not found in local.
* @author Florent Benoit
*
*/
public class SmartContextFactory implements InitialContextFactory {

    /**
     * Use the JDK logger (to avoid any dependency).
     */
    private static Logger logger = Logger.getLogger(SmartContextFactory.class.getName());

    /**
     * Default PROVIDER_URL.
     */
    private static final String DEFAULT_URL = "smart://localhost:2503";

    /**
     * Carol factory.
     */
    private static final String CAROL_FACTORY = "org.objectweb.carol.jndi.spi.MultiOrbInitialContextFactory";

    /**
     * EasyBeans delegating factory. (by default it will be ourself)
     */
    public static final String EASYBEANS_DELEGATING_FACTORY = "easybeans.smart.delegate.factory";

    /**
     * EasyBeans JNDI factory. (by default it will be Carol)
     */
    public static final String EASYBEANS_SMART_JNDI_FACTORY = "easybeans.smart.jndi.factory";

    /**
     * Use of a classloader for associating the smart info ?.
     */
    private static boolean useSmartInfoByClassLoader = false;

    /**
     * EasyBeans factory.
     */
    public static final String EASYBEANS_FACTORY = "easybeans.rpc.rmi.factory";

    /**
     * Map between a Smart Provider_URL and associated infos.
     */
    private static Map<String, SmartContextFactoryInfo> infos = new HashMap<String, SmartContextFactoryInfo>();

    /**
     * Map between a Smart Provider_URL and associated infos.
     */
    private static Map<ClassLoader, SmartContextFactoryInfo> infosClassLoader
        = new WeakHashMap<ClassLoader, SmartContextFactoryInfo>();

    /**
     * Provider URL to use by default.
     */
    private static String defaultProviderURL = null;


    /**
     * Default constructor.<br>
     * Sets the Portable RemoteObject Wrapper class too.
     */
    public SmartContextFactory() {
        // sets our class (or the delegating factory)
        System.setProperty(EASYBEANS_FACTORY, System.getProperty(EASYBEANS_DELEGATING_FACTORY, this.getClass().getName()));
        System.setProperty("javax.rmi.CORBA.PortableRemoteObjectClass", ProDelegate.class.getName());
    }

    /**
      * Creates an Initial Context for beginning name resolution.
      * Special requirements of this context are supplied
      * @param environment the given environment.
      * @return the context.
      * @throws NamingException if no context can be built.
    */
    @SuppressWarnings("unchecked")
    public Context getInitialContext(final Hashtable environment)
            throws NamingException {


        SmartContextFactoryInfo info = checkInit(environment);

        ClassLoader old = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(info.getClassLoader());

        // Previous JNDI Factory
        String oldInitialContextFactory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY);
        try {

            // JNDI Factory set in the environment ?
            String jndiFactory = (String) environment.get(EASYBEANS_SMART_JNDI_FACTORY);

            // JNDI factory set as system property ?
            if (jndiFactory == null) {
                jndiFactory = System.getProperty(EASYBEANS_SMART_JNDI_FACTORY);
            }

            // Else, use default factory
            if (jndiFactory == null) {
                jndiFactory = CAROL_FACTORY;
            }

            // set the right factory
            environment.put(Context.INITIAL_CONTEXT_FACTORY, jndiFactory);
            environment.put(Context.PROVIDER_URL, info.getProviderURL());

            // return wrapped context
            return new SmartContext(new InitialContext(environment), info.getClassLoader());
        } finally {
            Thread.currentThread().setContextClassLoader(old);
            // Restore InitialContext factory if defined
            if (oldInitialContextFactory != null) {
                System.setProperty(Context.INITIAL_CONTEXT_FACTORY, oldInitialContextFactory);
            }
        }

    }

    /**
     * Ensure that all is setup.
     * It has to work for each PROVIDER_URL
     * @param environment the InitialContext env.
     * @return data about the smart factory.
     * @throws NamingException if there is an exception
     */
    protected static SmartContextFactoryInfo checkInit(final Hashtable<?, ?> environment) throws NamingException {

        // Gets the provider url
        String currentProviderURL = (String) environment.get(Context.PROVIDER_URL);

        if (currentProviderURL == null) {
            if (defaultProviderURL == null) {
                logger.log(Level.WARNING, "No PROVIDER_URL setting found, use the default URL '" + DEFAULT_URL + "'.");
                currentProviderURL = DEFAULT_URL;
            } else {
                currentProviderURL = defaultProviderURL;
            }
        }

        // Data existing for the given provider URL ?
        SmartContextFactoryInfo info = null;

        // Use classloader as parameter ?
        if (useSmartInfoByClassLoader) {
            info = infosClassLoader.get(Thread.currentThread().getContextClassLoader());
        } else {
            info = infos.get(currentProviderURL);
        }

        // Not found, need to initialize
        if (info == null) {
            try {

                info = new SmartContextFactoryInfo();

                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "Initializing Smart Factory with remote URL '" + currentProviderURL + "'.");
                }

                // extract host
                String host = getHostOfUrl(currentProviderURL);

                // extract port
                int portNumber = getPortOfUrl(currentProviderURL);

                // build the classloader to use.
                AskingClassLoader classLoader = createClassLoader(host, portNumber);
                info.setClassLoader(classLoader);

                // Set the classloader for the ProDelegate class
                ProDelegate.setClassLoader(classLoader);

                String providerURL = classLoader.getProviderURL();
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, "Got remote PROVIDER_URL '" + providerURL + "'.");
                }
                info.setProviderURL(providerURL);

                // Add it to the list
                infos.put(currentProviderURL, info);
                // And add also the real provider URL
                infos.put(providerURL, info);

                if (useSmartInfoByClassLoader) {
                    infosClassLoader.put(Thread.currentThread().getContextClassLoader(), info);
                }

            } catch (Exception e) {
                NamingException ne = new NamingException("Cannot get a remote ClassLoader");
                ne.initCause(e);
                throw ne;
            }
        }

        return info;
    }

    /**
     * Create a classloader for the given host and port number within a privileged block.
     * @param host the given host
     * @param portNumber the port number
     * @return a new instance of the classloader
     */
    private static AskingClassLoader createClassLoader(final String host, final int portNumber) {
        return AccessController.doPrivileged(
                new PrivilegedAction<AskingClassLoader>() {
                    public AskingClassLoader run() {
                        return new AskingClassLoader(host, portNumber);
                    }
                }
        );
    }

    /**
     * Parses the given url, and returns the port number. 0 is given in error
     * case)
     * @param url given url on which extract port number
     * @return port number of the url
     * @throws NamingException if URL is invalid
     */
    public static int getPortOfUrl(final String url) throws NamingException {
        int portNumber = 0;
        try {
            StringTokenizer st = new StringTokenizer(url, ":");
            st.nextToken();
            st.nextToken();
            if (st.hasMoreTokens()) {
                StringTokenizer lastst = new StringTokenizer(st.nextToken(), "/");
                String pts = lastst.nextToken().trim();
                int i = pts.indexOf(',');
                if (i > 0) {
                    pts = pts.substring(0, i);
                }
                portNumber = new Integer(pts).intValue();
            }
            return portNumber;
        } catch (Exception e) {
            // don't rethrow original exception. only URL name is important
            throw new NamingException("Invalid URL '" + url + "'. It should be on the format <protocol>://<hostname>:<port>");
        }
    }

    /**
     * Parses the given url, and returns the hostname.
     * @param url given url on which extract hostname
     * @return hostname of the url
     * @throws NamingException if URL is invalid
     */
    private static String getHostOfUrl(final String url) throws NamingException {
        String host = null;
        // this would be simpler with a regexp :)
        try {
            // url is of the form protocol://<hostname>:<port>
            String[] tmpSplitStr = url.split(":");

            // array should be of length = 3
            // get 2nd element (should be //<hostname>)
            String tmpHost = tmpSplitStr[1];

            // remove //
            String[] tmpSplitHost = tmpHost.split("/");

            // Get last element of the array to get hostname
            host = tmpSplitHost[tmpSplitHost.length - 1];
        } catch (Exception e) {
            // don't rethrow original exception. only URL name is important
            throw new NamingException("Invalid URL '" + url + "'. It should be on the format <protocol>://<hostname>:<port>");
        }
        return host;
    }

    /**
     * Allow to change the provider URL used by default.
     * @param providerURL the given provider URL
     */
    public static void setDefaultProviderUrl(final String providerURL) {
        defaultProviderURL = providerURL;
    }

    /**
     * Use the classloader as a key for AskingClassloader ?.
     * @param useOfClassLoader the given value.
     */
    public static void setUseClassLoader(final boolean useOfClassLoader) {
        useSmartInfoByClassLoader = useOfClassLoader;
    }
}
TOP

Related Classes of org.ow2.easybeans.component.smartclient.spi.SmartContextFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.