org.eclipse.gemini.blueprint.extender.internal.support.NamespacePlugins.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.gemini.blueprint.extender.internal.support.NamespacePlugins.java

Source

/******************************************************************************
 * Copyright (c) 2006, 2010 VMware Inc., Oracle Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution. 
 * The Eclipse Public License is available at 
 * http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
 * is available at http://www.opensource.org/licenses/apache2.0.php.
 * You may elect to redistribute this code under either of these licenses. 
 * 
 * Contributors:
 *   VMware Inc.
 *   Oracle Inc.
 *****************************************************************************/

package org.eclipse.gemini.blueprint.extender.internal.support;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.Bundle;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver;
import org.springframework.beans.factory.xml.DelegatingEntityResolver;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerResolver;
import org.eclipse.gemini.blueprint.util.BundleDelegatingClassLoader;
import org.eclipse.gemini.blueprint.util.OsgiStringUtils;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Spring schema handler/resolver for OSGi environments.
 * 
 * Besides delegation this class also does type filtering to avoid wiring the wrong bundle if multiple versions of the
 * same library (which support the same schema) are available.
 * 
 * Additionally, lazy handlers are supported so that they are checked (and thus loaded) only if no previous handler has
 * been able to satisfy the request.
 * 
 * @author Hal Hildebrand
 * @author Costin Leau
 * 
 */
public class NamespacePlugins implements NamespaceHandlerResolver, EntityResolver, DisposableBean {

    /**
     * Wrapper class which implements both {@link EntityResolver} and {@link NamespaceHandlerResolver} interfaces.
     * 
     * Simply delegates to the actual implementation discovered in a specific bundle.
     */
    private static class Plugin implements NamespaceHandlerResolver, EntityResolver {

        private final NamespaceHandlerResolver namespace;

        private final EntityResolver entity;

        private final Bundle bundle;

        private Plugin(Bundle bundle) {
            this.bundle = bundle;

            ClassLoader loader = BundleDelegatingClassLoader.createBundleClassLoaderFor(bundle);

            entity = new DelegatingEntityResolver(loader);
            namespace = new DefaultNamespaceHandlerResolver(loader);
        }

        public NamespaceHandler resolve(String namespaceUri) {
            return namespace.resolve(namespaceUri);
        }

        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
            return entity.resolveEntity(publicId, systemId);
        }

        public Bundle getBundle() {
            return bundle;
        }
    }

    private static final Log log = LogFactory.getLog(NamespacePlugins.class);

    final LazyBundleRegistry.Condition condition = new LazyBundleRegistry.Condition() {

        private final String NS_HANDLER_RESOLVER_CLASS_NAME = NamespaceHandlerResolver.class.getName();

        public boolean pass(Bundle bundle) {
            try {
                Class<?> type = bundle.loadClass(NS_HANDLER_RESOLVER_CLASS_NAME);
                return NamespaceHandlerResolver.class.equals(type);
            } catch (Throwable th) {
                // if the interface is not wired, ignore the bundle
                log.warn("Bundle " + OsgiStringUtils.nullSafeNameAndSymName(bundle) + " cannot see class ["
                        + NS_HANDLER_RESOLVER_CLASS_NAME + "]; ignoring it as a namespace resolver");

                return false;
            }
        }
    };

    private final LazyBundleRegistry.Activator<Plugin> activation = new LazyBundleRegistry.Activator<Plugin>() {

        public Plugin activate(Bundle bundle) {
            return new Plugin(bundle);
        }
    };

    private final LazyBundleRegistry<Plugin> pluginRegistry = new LazyBundleRegistry<Plugin>(condition, activation,
            log);

    /**
     * Adds a bundle as a handler to plugin registry.
     * 
     * @param bundle
     * @param lazyBundle
     */
    void addPlugin(Bundle bundle, boolean lazyBundle, boolean applyCondition) {
        boolean debug = log.isDebugEnabled();

        if (debug)
            log.debug("Adding as " + (lazyBundle ? "lazy " : "") + "namespace handler bundle "
                    + OsgiStringUtils.nullSafeNameAndSymName(bundle));

        pluginRegistry.add(bundle, lazyBundle, applyCondition);
    }

    /**
     * Checks the type compatibility check between the namespace parser wired to Spring DM and the discovered bundle
     * class space.
     * 
     * @param bundle handler bundle
     * @return true if there is type compatibility, false otherwise
     */
    boolean isTypeCompatible(Bundle bundle) {
        return condition.pass(bundle);
    }

    /**
     * Returns true if a handler mapping was removed for the given bundle.
     * 
     * @param bundle bundle to look at
     * @return true if the bundle was used in the plugin map
     */
    boolean removePlugin(Bundle bundle) {
        if (log.isDebugEnabled())
            log.debug("Removing handler " + OsgiStringUtils.nullSafeNameAndSymName(bundle));

        return pluginRegistry.remove(bundle);
    }

    public NamespaceHandler resolve(final String namespaceUri) {
        if (System.getSecurityManager() != null) {
            return AccessController.doPrivileged(new PrivilegedAction<NamespaceHandler>() {

                public NamespaceHandler run() {
                    return doResolve(namespaceUri);
                }
            });

        } else {
            return doResolve(namespaceUri);
        }
    }

    public InputSource resolveEntity(final String publicId, final String systemId)
            throws SAXException, IOException {
        if (System.getSecurityManager() != null) {
            try {
                return AccessController.doPrivileged(new PrivilegedExceptionAction<InputSource>() {

                    public InputSource run() throws Exception {
                        return doResolveEntity(publicId, systemId);
                    }
                });
            } catch (PrivilegedActionException pae) {
                Exception cause = pae.getException();
                handleInputSourceException(cause);
            }
        } else {
            try {
                return doResolveEntity(publicId, systemId);
            } catch (Exception ex) {
                handleInputSourceException(ex);
            }
        }

        return null;
    }

    private NamespaceHandler doResolve(final String namespaceUri) {
        final boolean debug = log.isDebugEnabled();
        final boolean trace = log.isTraceEnabled();

        if (debug)
            log.debug("Trying to resolving namespace handler for " + namespaceUri);

        try {
            return pluginRegistry.apply(new LazyBundleRegistry.Operation<Plugin, NamespaceHandler>() {

                public NamespaceHandler operate(Plugin plugin) {
                    try {
                        NamespaceHandler handler = plugin.resolve(namespaceUri);
                        if (handler != null) {
                            if (debug)
                                log.debug("Namespace handler for " + namespaceUri + " found inside bundle "
                                        + OsgiStringUtils.nullSafeNameAndSymName(plugin.getBundle()));

                            return handler;
                        } else if (trace)
                            log.trace("Namespace handler for " + namespaceUri + " not found inside bundle "
                                    + OsgiStringUtils.nullSafeNameAndSymName(plugin.getBundle()));

                    } catch (IllegalArgumentException ex) {
                        if (trace)
                            log.trace("Namespace handler for " + namespaceUri + " not found inside bundle "
                                    + OsgiStringUtils.nullSafeNameAndSymName(plugin.getBundle()));
                    }
                    return null;
                }
            });
        } catch (Exception ex) {
            // the inner method doesn't declare any exceptions so the cast should be safe
            throw (RuntimeException) ex;
        }
    }

    private InputSource doResolveEntity(final String publicId, final String systemId) throws Exception {
        final boolean debug = log.isDebugEnabled();
        final boolean trace = log.isTraceEnabled();

        if (debug)
            log.debug("Trying to resolving entity for " + publicId + "|" + systemId);

        if (systemId != null) {

            return pluginRegistry.apply(new LazyBundleRegistry.Operation<Plugin, InputSource>() {

                public InputSource operate(Plugin plugin) throws SAXException, IOException {
                    try {
                        InputSource inputSource = plugin.resolveEntity(publicId, systemId);

                        if (inputSource != null) {
                            if (debug)
                                log.debug("XML schema for " + publicId + "|" + systemId + " found inside bundle "
                                        + OsgiStringUtils.nullSafeNameAndSymName(plugin.getBundle()));
                            return inputSource;
                        }

                    } catch (FileNotFoundException ex) {
                        if (trace)
                            log.trace("XML schema for " + publicId + "|" + systemId + " not found inside bundle "
                                    + OsgiStringUtils.nullSafeNameAndSymName(plugin.getBundle()), ex);
                    }
                    return null;
                }
            });
        }
        return null;
    }

    private void handleInputSourceException(Exception exception) throws SAXException, IOException {
        if (exception instanceof RuntimeException) {
            throw (RuntimeException) exception;
        }
        if (exception instanceof IOException) {
            throw (IOException) exception;
        }
        throw (SAXException) exception;
    }

    public void destroy() {
        pluginRegistry.clear();
    }
}