/*******************************************************************************
* 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.IOException;
import java.util.Enumeration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.springframework.beans.factory.xml.NamespaceHandlerResolver;
import org.springframework.ide.eclipse.beans.core.model.INamespaceDefinitionResolver;
import org.springframework.util.Assert;
import org.xml.sax.EntityResolver;
/**
* Support class that deals with namespace parsers discovered inside Spring bundles.
*
* @author Christian Dupuis
* @author Costin Leau
* @author Martin Lippert
*/
public class NamespaceManager {
/** The set of all namespace plugins known to the extender */
private ToolingAwareNamespacePlugins namespacePlugins;
/**
* ServiceRegistration object returned by OSGi when registering the NamespacePlugins instance as
* a service
*/
private ServiceRegistration<?> nsResolverRegistration, enResolverRegistration = null;
private ServiceRegistration<?> ndResolverRegistration;
/** OSGi Environment */
private final BundleContext context;
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;
// detect package admin
this.namespacePlugins = new ToolingAwareNamespacePlugins();
}
/**
* 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 (isSystemBundle(bundle)) {
return;
}
// FIXME: RFC-124 big 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;
// 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;
}
catch (IOException 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 (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 {
// bundle declares only schemas, add it though the handlers might not be compatible...
if (hasSchemas)
this.namespacePlugins.addPlugin(bundle, isLazyBundle, false);
}
}
private boolean isSystemBundle(Bundle bundle) {
Assert.notNull(bundle);
return (bundle.getBundleId() == 0);
}
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);
this.namespacePlugins.removePlugin(bundle);
}
/**
* Registers the NamespacePlugins instance as an Osgi Resolver service
*/
private void registerResolverServices() {
nsResolverRegistration = context.registerService(
new String[] { NamespaceHandlerResolver.class.getName() }, this.namespacePlugins,
null);
enResolverRegistration = context.registerService(new String[] { EntityResolver.class
.getName() }, this.namespacePlugins, null);
ndResolverRegistration = context.registerService(
new String[] { INamespaceDefinitionResolver.class.getName() },
this.namespacePlugins, null);
}
/**
* Unregisters the NamespaceHandler and EntityResolver service
*/
private void unregisterResolverService() {
unregisterService(nsResolverRegistration);
unregisterService(enResolverRegistration);
unregisterService(ndResolverRegistration);
this.nsResolverRegistration = null;
this.enResolverRegistration = null;
this.ndResolverRegistration = null;
}
public boolean unregisterService(ServiceRegistration<?> registration) {
try {
if (registration != null) {
registration.unregister();
return true;
}
}
catch (IllegalStateException alreadyUnregisteredException) {
}
return false;
}
public ToolingAwareNamespacePlugins getNamespacePlugins() {
return namespacePlugins;
}
//
// Lifecycle methods
//
public void afterPropertiesSet() {
registerResolverServices();
}
public void destroy() {
unregisterResolverService();
this.namespacePlugins.destroy();
this.namespacePlugins = null;
}
}