/******************************************************************************* * Copyright (c) 2009, 2013 Spring IDE Developers * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.beans.core.internal.model.namespaces; 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.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.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 Christian Dupuis * @author Hal Hildebrand * @author Costin Leau */ public class NamespacePlugins implements NamespaceHandlerResolver, EntityResolver, DisposableBean { private final LazyBundleRegistry.Activator<Plugin> activation = new LazyBundleRegistry.Activator<Plugin>() { public Plugin activate(Bundle bundle) { return new Plugin(bundle); } }; 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 return false; } } }; private final LazyBundleRegistry<Plugin> pluginRegistry = new LazyBundleRegistry<Plugin>(condition, activation); public void destroy() { pluginRegistry.clear(); } public NamespaceHandler resolve(final String namespaceUri) { if (System.getSecurityManager() != null) { return AccessController.doPrivileged(new PrivilegedAction<NamespaceHandler>() { public NamespaceHandler run() { return doResolve(namespaceUri); } }); } 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) { try { return pluginRegistry.apply(new LazyBundleRegistry.Operation<Plugin, NamespaceHandler>() { public NamespaceHandler operate(Plugin plugin) { try { NamespaceHandler handler = plugin.resolve(namespaceUri); if (handler != null) { return handler; } } catch (IllegalArgumentException ex) { } 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 { 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) { return inputSource; } } catch (FileNotFoundException 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; } /** * Adds a bundle as a handler to plugin registry. * @param bundle * @param lazyBundle */ void addPlugin(Bundle bundle, boolean lazyBundle, boolean applyCondition) { 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) { return pluginRegistry.remove(bundle); } /** * 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 EntityResolver entity; private final NamespaceHandlerResolver namespace; private Plugin(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); } } }