| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.aries.blueprint.spring; |
| |
| import java.io.IOException; |
| import java.net.URI; |
| import java.net.URL; |
| import java.util.Collections; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import org.apache.aries.blueprint.ComponentDefinitionRegistry; |
| import org.apache.aries.blueprint.NamespaceHandler; |
| import org.apache.aries.blueprint.NamespaceHandler2; |
| import org.apache.aries.blueprint.ParserContext; |
| import org.apache.aries.blueprint.PassThroughMetadata; |
| import org.apache.aries.blueprint.mutable.MutableBeanMetadata; |
| import org.apache.aries.blueprint.mutable.MutablePassThroughMetadata; |
| import org.apache.aries.blueprint.mutable.MutableRefMetadata; |
| import org.apache.aries.blueprint.parser.Parser; |
| import org.apache.aries.blueprint.parser.ParserContextImpl; |
| import org.apache.aries.blueprint.services.ExtendedBlueprintContainer; |
| import org.osgi.framework.Bundle; |
| import org.osgi.service.blueprint.reflect.BeanMetadata; |
| import org.osgi.service.blueprint.reflect.ComponentMetadata; |
| import org.osgi.service.blueprint.reflect.Metadata; |
| import org.springframework.beans.factory.BeanFactory; |
| import org.springframework.beans.factory.config.BeanDefinition; |
| import org.springframework.beans.factory.parsing.EmptyReaderEventListener; |
| import org.springframework.beans.factory.parsing.FailFastProblemReporter; |
| import org.springframework.beans.factory.parsing.NullSourceExtractor; |
| import org.springframework.beans.factory.parsing.ProblemReporter; |
| import org.springframework.beans.factory.parsing.ReaderEventListener; |
| import org.springframework.beans.factory.parsing.SourceExtractor; |
| import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
| import org.springframework.beans.factory.support.DefaultListableBeanFactory; |
| import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; |
| import org.springframework.beans.factory.xml.NamespaceHandlerResolver; |
| import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; |
| import org.springframework.beans.factory.xml.XmlReaderContext; |
| import org.springframework.context.ApplicationContext; |
| import org.springframework.core.io.Resource; |
| import org.springframework.core.io.UrlResource; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.xml.sax.EntityResolver; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * Blueprint NamespaceHandler wrapper for a spring NamespaceHandler |
| */ |
| public class BlueprintNamespaceHandler implements NamespaceHandler, NamespaceHandler2 { |
| |
| public static final String SPRING_CONTEXT_ID = "." + org.springframework.beans.factory.xml.ParserContext.class.getName(); |
| public static final String SPRING_BEAN_PROCESSOR_ID = "." + SpringBeanProcessor.class.getName(); |
| public static final String SPRING_APPLICATION_CONTEXT_ID = "." + ApplicationContext.class.getName(); |
| public static final String SPRING_BEAN_FACTORY_ID = "." + BeanFactory.class.getName(); |
| |
| private final Bundle bundle; |
| private final Properties schemas; |
| private final org.springframework.beans.factory.xml.NamespaceHandler springHandler; |
| |
| public BlueprintNamespaceHandler(Bundle bundle, Properties schemas, org.springframework.beans.factory.xml.NamespaceHandler springHandler) { |
| this.bundle = bundle; |
| this.schemas = schemas; |
| this.springHandler = springHandler; |
| springHandler.init(); |
| } |
| |
| public org.springframework.beans.factory.xml.NamespaceHandler getSpringHandler() { |
| return springHandler; |
| } |
| |
| @Override |
| public boolean usePsvi() { |
| return true; |
| } |
| |
| @Override |
| public URL getSchemaLocation(String s) { |
| if (schemas.containsKey(s)) { |
| return bundle.getResource(schemas.getProperty(s)); |
| } |
| return null; |
| } |
| |
| @Override |
| public Set<Class> getManagedClasses() { |
| return Collections.<Class>singleton(BeanDefinition.class); |
| } |
| |
| @Override |
| public Metadata parse(Element element, ParserContext parserContext) { |
| try { |
| // Get the spring context |
| org.springframework.beans.factory.xml.ParserContext springContext |
| = getOrCreateParserContext(parserContext); |
| // Parse spring bean |
| BeanDefinition bd = springHandler.parse(element, springContext); |
| for (String name : springContext.getRegistry().getBeanDefinitionNames()) { |
| if (springContext.getRegistry().getBeanDefinition(name) == bd) { |
| ComponentDefinitionRegistry registry = parserContext.getComponentDefinitionRegistry(); |
| if (registry.containsComponentDefinition(name)) { |
| // Hack: we can't really make the difference between a top level bean |
| // and an inlined bean when using custom (eventually nested) namespaces. |
| // To work around the problem, the BlueprintBeanFactory will always register |
| // a BeanMetadata for each bean, but here, we unregister it and return it instead |
| // so that the caller is responsible for registering the metadata. |
| ComponentMetadata metadata = registry.getComponentDefinition(name); |
| registry.removeComponentDefinition(name); |
| return metadata; |
| } |
| } |
| } |
| return null; |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Override |
| public ComponentMetadata decorate(Node node, ComponentMetadata componentMetadata, ParserContext parserContext) { |
| return componentMetadata; |
| } |
| |
| private org.springframework.beans.factory.xml.ParserContext getOrCreateParserContext(ParserContext parserContext) { |
| ComponentDefinitionRegistry registry = parserContext.getComponentDefinitionRegistry(); |
| ExtendedBlueprintContainer container = getBlueprintContainer(parserContext); |
| // Create spring application context |
| SpringApplicationContext applicationContext = getPassThrough(parserContext, |
| SPRING_APPLICATION_CONTEXT_ID, SpringApplicationContext.class); |
| if (applicationContext == null) { |
| applicationContext = new SpringApplicationContext(container); |
| registry.registerComponentDefinition(createPassThrough(parserContext, |
| SPRING_APPLICATION_CONTEXT_ID, applicationContext, "destroy" |
| )); |
| } |
| // Create registry |
| DefaultListableBeanFactory beanFactory = getPassThrough(parserContext, |
| SPRING_BEAN_FACTORY_ID, DefaultListableBeanFactory.class); |
| if (beanFactory == null) { |
| beanFactory = applicationContext.getBeanFactory(); |
| registry.registerComponentDefinition(createPassThrough(parserContext, |
| SPRING_BEAN_FACTORY_ID, beanFactory |
| )); |
| } |
| // Create spring parser context |
| org.springframework.beans.factory.xml.ParserContext springParserContext |
| = getPassThrough(parserContext, SPRING_CONTEXT_ID, org.springframework.beans.factory.xml.ParserContext.class); |
| if (springParserContext == null) { |
| // Create spring context |
| springParserContext = createSpringParserContext(parserContext, beanFactory); |
| registry.registerComponentDefinition(createPassThrough(parserContext, |
| SPRING_CONTEXT_ID, springParserContext |
| )); |
| } |
| // Create processor |
| if (!parserContext.getComponentDefinitionRegistry().containsComponentDefinition(SPRING_BEAN_PROCESSOR_ID)) { |
| MutableBeanMetadata bm = parserContext.createMetadata(MutableBeanMetadata.class); |
| bm.setId(SPRING_BEAN_PROCESSOR_ID); |
| bm.setProcessor(true); |
| bm.setScope(BeanMetadata.SCOPE_SINGLETON); |
| bm.setRuntimeClass(SpringBeanProcessor.class); |
| bm.setActivation(BeanMetadata.ACTIVATION_EAGER); |
| bm.addArgument(createRef(parserContext, "blueprintBundleContext"), null, 0); |
| bm.addArgument(createRef(parserContext, "blueprintContainer"), null, 0); |
| bm.addArgument(createRef(parserContext, SPRING_APPLICATION_CONTEXT_ID), null, 0); |
| registry.registerComponentDefinition(bm); |
| } |
| // Add the namespace handlers' bundle to the application context classloader |
| for (URI uri : parserContext.getNamespaces()) { |
| NamespaceHandler ns = parserContext.getNamespaceHandler(uri); |
| if (ns instanceof BlueprintNamespaceHandler) { |
| applicationContext.addSourceBundle(((BlueprintNamespaceHandler) ns).bundle); |
| } |
| } |
| return springParserContext; |
| } |
| |
| private ComponentMetadata createPassThrough(ParserContext parserContext, String id, Object o) { |
| MutablePassThroughMetadata pt = parserContext.createMetadata(MutablePassThroughMetadata.class); |
| pt.setId(id); |
| pt.setObject(o); |
| return pt; |
| } |
| |
| private ComponentMetadata createPassThrough(ParserContext parserContext, String id, Object o, String destroy) { |
| MutablePassThroughMetadata pt = parserContext.createMetadata(MutablePassThroughMetadata.class); |
| pt.setId(id + ".factory"); |
| pt.setObject(new Holder(o)); |
| MutableBeanMetadata b = parserContext.createMetadata(MutableBeanMetadata.class); |
| b.setId(id); |
| b.setFactoryComponent(pt); |
| b.setFactoryMethod("getObject"); |
| b.setDestroyMethod(destroy); |
| return b; |
| } |
| |
| public static class Holder { |
| private final Object object; |
| |
| public Holder(Object object) { |
| this.object = object; |
| } |
| |
| public Object getObject() { |
| return object; |
| } |
| } |
| |
| private Metadata createRef(ParserContext parserContext, String id) { |
| MutableRefMetadata ref = parserContext.createMetadata(MutableRefMetadata.class); |
| ref.setComponentId(id); |
| return ref; |
| } |
| |
| private ExtendedBlueprintContainer getBlueprintContainer(ParserContext parserContext) { |
| ExtendedBlueprintContainer container = getPassThrough(parserContext, "blueprintContainer", ExtendedBlueprintContainer.class); |
| if (container == null) { |
| throw new IllegalStateException(); |
| } |
| return container; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private <T> T getPassThrough(ParserContext parserContext, String name, Class<T> clazz) { |
| Metadata metadata = parserContext.getComponentDefinitionRegistry().getComponentDefinition(name); |
| if (metadata instanceof BeanMetadata) { |
| BeanMetadata bm = (BeanMetadata) metadata; |
| if (bm.getFactoryComponent() instanceof PassThroughMetadata |
| && "getObject".equals(bm.getFactoryMethod())) { |
| metadata = bm.getFactoryComponent(); |
| } |
| } |
| if (metadata instanceof PassThroughMetadata) { |
| Object o = ((PassThroughMetadata) metadata).getObject(); |
| if (o instanceof Holder) { |
| o = ((Holder) o).getObject(); |
| } |
| return (T) o; |
| } else { |
| return null; |
| } |
| } |
| |
| private org.springframework.beans.factory.xml.ParserContext createSpringParserContext(final ParserContext parserContext, DefaultListableBeanFactory registry) { |
| try { |
| SpringVersionBridgeXmlBeanDefinitionReader xbdr = new SpringVersionBridgeXmlBeanDefinitionReader(registry); |
| Resource resource = new UrlResource(parserContext.getSourceNode().getOwnerDocument().getDocumentURI()); |
| ProblemReporter problemReporter = new FailFastProblemReporter(); |
| ReaderEventListener listener = new EmptyReaderEventListener(); |
| SourceExtractor extractor = new NullSourceExtractor(); |
| NamespaceHandlerResolver resolver = new SpringNamespaceHandlerResolver(parserContext); |
| xbdr.setProblemReporter(problemReporter); |
| xbdr.setEventListener(listener); |
| xbdr.setSourceExtractor(extractor); |
| xbdr.setNamespaceHandlerResolver(resolver); |
| xbdr.setEntityResolver(new EntityResolver() { |
| @Override |
| public InputSource resolveEntity(String publicId, String systemId) throws IOException { |
| for (URI ns : parserContext.getNamespaces()) { |
| NamespaceHandler nh = parserContext.getNamespaceHandler(ns); |
| URL url = null; |
| if (publicId != null) { |
| url = nh.getSchemaLocation(publicId); |
| } |
| if (url == null && systemId != null) { |
| url = nh.getSchemaLocation(systemId); |
| } |
| if (url != null) { |
| InputSource is = new InputSource(); |
| is.setPublicId(publicId); |
| is.setSystemId(systemId); |
| is.setByteStream(url.openStream()); |
| return is; |
| } |
| } |
| return null; |
| } |
| }); |
| XmlReaderContext xmlReaderContext = xbdr.createReaderContext(resource); |
| BeanDefinitionParserDelegate bdpd = new BeanDefinitionParserDelegate(xmlReaderContext); |
| return new org.springframework.beans.factory.xml.ParserContext(xmlReaderContext, bdpd); |
| } catch (Exception e) { |
| throw new RuntimeException("Error creating spring parser context", e); |
| } |
| } |
| |
| /** |
| * Some methods are protected in Spring 3.x, hence overridden methods in this class to make them available. |
| */ |
| private class SpringVersionBridgeXmlBeanDefinitionReader extends XmlBeanDefinitionReader { |
| |
| public SpringVersionBridgeXmlBeanDefinitionReader(final BeanDefinitionRegistry registry) { |
| super(registry); |
| } |
| |
| @Override |
| public XmlReaderContext createReaderContext(final Resource resource) { |
| return super.createReaderContext(resource); |
| } |
| } |
| |
| |
| } |