/****************************************************************************** * Copyright (c) 2006, 2010 VMware 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. *****************************************************************************/ package org.eclipse.gemini.blueprint.extender.internal.support; import java.io.IOException; import java.util.Collections; import java.util.Enumeration; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.xml.NamespaceHandlerResolver; import org.eclipse.gemini.blueprint.extender.internal.util.BundleUtils; import org.eclipse.gemini.blueprint.util.OsgiBundleUtils; import org.eclipse.gemini.blueprint.util.OsgiServiceUtils; import org.eclipse.gemini.blueprint.util.OsgiStringUtils; import org.springframework.util.Assert; import org.xml.sax.EntityResolver; /** * Support class that deals with namespace parsers discovered inside Spring bundles. * * @author Costin Leau * */ public class NamespaceManager implements InitializingBean, DisposableBean { private static final Log log = LogFactory.getLog(NamespaceManager.class); /** The set of all namespace plugins known to the extender */ private NamespacePlugins namespacePlugins; /** * ServiceRegistration object returned by OSGi when registering the NamespacePlugins instance as a service */ private ServiceRegistration nsResolverRegistration, enResolverRegistration = null; /** OSGi Environment */ private final BundleContext context; private final String extenderInfo; private static final String META_INF = "META-INF/"; private static final String SPRING_HANDLERS = "spring.handlers"; private static final String SPRING_SCHEMAS = "spring.schemas"; /** * Constructs a new <code>NamespaceManager</code> instance. * * @param context containing bundle context */ public NamespaceManager(BundleContext context) { this.context = context; extenderInfo = context.getBundle().getSymbolicName() + "|" + OsgiBundleUtils.getBundleVersion(context.getBundle()); // detect package admin this.namespacePlugins = new NamespacePlugins(); } /** * Registers the namespace plugin handler if this bundle defines handler mapping or schema mapping resources. * * <p/> This method considers only the bundle space and not the class space. * * @param bundle target bundle * @param isLazyBundle indicator if the bundle analyzed is lazily activated */ public void maybeAddNamespaceHandlerFor(Bundle bundle, boolean isLazyBundle) { // Ignore system bundle if (OsgiBundleUtils.isSystemBundle(bundle)) { return; } // Ignore non-wired Spring DM bundles if ("org.eclipse.gemini.blueprint.core".equals(bundle.getSymbolicName()) && !bundle.equals(BundleUtils.getDMCoreBundle(context))) { return; } boolean debug = log.isDebugEnabled(); boolean trace = log.isTraceEnabled(); // FIXME: Blueprint uber bundle temporary hack // since embedded libraries are not discovered by findEntries and inlining them doesn't work // (due to resource classes such as namespace handler definitions) // we use getResource boolean hasHandlers = false, hasSchemas = false; if (trace) { log.trace("Inspecting bundle " + bundle + " for Spring namespaces"); } // extender/RFC 124 bundle if (context.getBundle().equals(bundle)) { try { Enumeration<?> handlers = bundle.getResources(META_INF + SPRING_HANDLERS); Enumeration<?> schemas = bundle.getResources(META_INF + SPRING_SCHEMAS); hasHandlers = handlers != null; hasSchemas = schemas != null; if (hasHandlers && debug) { log.debug("Found namespace handlers: " + Collections.list(schemas)); } } catch (IOException ioe) { log.warn("Cannot discover own namespaces", ioe); } } else { hasHandlers = bundle.findEntries(META_INF, SPRING_HANDLERS, false) != null; hasSchemas = bundle.findEntries(META_INF, SPRING_SCHEMAS, false) != null; } // if the bundle defines handlers if (hasHandlers) { if (trace) log.trace("Bundle " + bundle + " provides Spring namespace handlers..."); if (isLazyBundle) { this.namespacePlugins.addPlugin(bundle, isLazyBundle, true); } else { // check type compatibility between the bundle's and spring-extender's spring version if (hasCompatibleNamespaceType(bundle)) { this.namespacePlugins.addPlugin(bundle, isLazyBundle, false); } else { if (debug) log.debug("Bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "] declares namespace handlers but is not compatible with extender [" + extenderInfo + "]; ignoring..."); } } } else { // bundle declares only schemas, add it though the handlers might not be compatible... if (hasSchemas) { this.namespacePlugins.addPlugin(bundle, isLazyBundle, false); if (trace) log.trace("Bundle " + bundle + " provides Spring schemas..."); } } } private boolean hasCompatibleNamespaceType(Bundle bundle) { return namespacePlugins.isTypeCompatible(bundle); } /** * Removes the target bundle from the set of those known to provide handler or schema mappings. * * @param bundle handler bundle */ public void maybeRemoveNameSpaceHandlerFor(Bundle bundle) { Assert.notNull(bundle); boolean removed = this.namespacePlugins.removePlugin(bundle); if (removed && log.isDebugEnabled()) { log.debug("Removed namespace handler resolver for " + OsgiStringUtils.nullSafeNameAndSymName(bundle)); } } /** * Registers the NamespacePlugins instance as an Osgi Resolver service */ private void registerResolverServices() { if (log.isDebugEnabled()) { log.debug("Registering Spring NamespaceHandlerResolver and EntityResolver..."); } Bundle bnd = BundleUtils.getDMCoreBundle(context); Properties props = null; if (bnd != null) { props = new Properties(); props.setProperty(BundleUtils.DM_CORE_ID, "" + bnd.getBundleId()); props.setProperty(BundleUtils.DM_CORE_TS, "" + bnd.getLastModified()); } nsResolverRegistration = context.registerService(new String[] { NamespaceHandlerResolver.class.getName() }, this.namespacePlugins, props); enResolverRegistration = context.registerService(new String[] { EntityResolver.class.getName() }, this.namespacePlugins, props); } /** * Unregisters the NamespaceHandler and EntityResolver service */ private void unregisterResolverService() { boolean result = OsgiServiceUtils.unregisterService(nsResolverRegistration); result = result || OsgiServiceUtils.unregisterService(enResolverRegistration); if (result) { if (log.isDebugEnabled()) log.debug("Unregistering Spring NamespaceHandler and EntityResolver service"); } this.nsResolverRegistration = null; this.enResolverRegistration = null; } public NamespacePlugins getNamespacePlugins() { return namespacePlugins; } // // Lifecycle methods // public void afterPropertiesSet() { registerResolverServices(); } public void destroy() { unregisterResolverService(); this.namespacePlugins.destroy(); this.namespacePlugins = null; } }