/* * Copyright 2013 NGDATA nv * Copyright 2008 Outerthought bvba and Schaubroeck nv * * Licensed 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.lilyproject.runtime.module.build; import java.io.InputStream; import java.lang.reflect.Proxy; import java.net.MalformedURLException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.lilyproject.runtime.LilyRTException; import org.lilyproject.runtime.LilyRuntime; import org.lilyproject.runtime.module.Module; import org.lilyproject.runtime.module.ModuleConfig; import org.lilyproject.runtime.module.ModuleImpl; import org.lilyproject.runtime.module.javaservice.JavaServiceShield; import org.lilyproject.runtime.rapi.ModuleSource; import org.lilyproject.runtime.repository.ArtifactNotFoundException; import org.lilyproject.util.io.IOUtils; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.io.InputStreamResource; public class ModuleBuilder { // These ThreadLocal's serve as communication mechanism for LilyRuntimeNamespaceHandler protected static ThreadLocal<SpringBuildContext> SPRING_BUILD_CONTEXT = new ThreadLocal<SpringBuildContext>(); protected final Log infolog = LogFactory.getLog(LilyRuntime.INFO_LOG_CATEGORY); private final Log log = LogFactory.getLog(getClass()); private ModuleBuilder() { // private constructor to avoid instantiation } public static Module build(ModuleConfig cfg, ClassLoader classLoader, LilyRuntime runtime) throws ArtifactNotFoundException, MalformedURLException { return new ModuleBuilder().buildInt(cfg, classLoader, runtime); } private Module buildInt(ModuleConfig cfg, ClassLoader classLoader, LilyRuntime runtime) throws ArtifactNotFoundException, MalformedURLException { infolog.info("Starting module " + cfg.getId() + " - " + cfg.getLocation()); ClassLoader previousContextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(classLoader); GenericApplicationContext applicationContext = new GenericApplicationContext(); applicationContext.setDisplayName(cfg.getId()); applicationContext.setClassLoader(classLoader); // Note: before loading any beans in the spring container: // * the spring build context needs access to the module, for possible injection & module-protocol resolving during bean initialization // * the module also needs to have the reference to the applicationcontext, as there might be beans trying to get while initializing ModuleImpl module = new ModuleImpl(classLoader, applicationContext, cfg.getDefinition(), cfg.getModuleSource()); SpringBuildContext springBuildContext = new SpringBuildContext(runtime, module, classLoader); SPRING_BUILD_CONTEXT.set(springBuildContext); XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(applicationContext); xmlReader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); xmlReader.setBeanClassLoader(classLoader); for (ModuleSource.SpringConfigEntry entry : cfg.getModuleSource().getSpringConfigs(runtime.getMode())) { InputStream is = entry.getStream(); try { xmlReader.loadBeanDefinitions(new InputStreamResource(is, entry.getLocation() + " in " + cfg.getDefinition().getFile().getAbsolutePath())); } finally { IOUtils.closeQuietly(is, entry.getLocation()); } } applicationContext.refresh(); // Handle the service exports for (SpringBuildContext.JavaServiceExport entry : springBuildContext.getExportedJavaServices()) { Class serviceType = entry.serviceType; if (!serviceType.isInterface()) { throw new LilyRTException("Exported service is not an interface: " + serviceType.getName()); } String beanName = entry.beanName; Object component; try { component = applicationContext.getBean(beanName); } catch (NoSuchBeanDefinitionException e) { throw new LilyRTException("Bean not found for service to export, service type " + serviceType.getName() + ", bean name " + beanName, e); } if (!serviceType.isAssignableFrom(component.getClass())) { throw new LilyRTException("Exported service does not implemented specified type interface. Bean = " + beanName + ", interface = " + serviceType.getName()); } infolog.debug(" exporting bean " + beanName + " for service " + serviceType.getName()); Object service = shieldJavaService(serviceType, component, module, classLoader); runtime.getJavaServiceManager().addService(serviceType, cfg.getId(), entry.name, service); } module.start(); return module; } catch (Throwable e) { // TODO module source and classloader handle might need disposing! // especially important if the lily runtime is launched as part of a longer-living VM throw new LilyRTException("Error constructing module defined at " + cfg.getDefinition().getFile().getAbsolutePath(), e); } finally { Thread.currentThread().setContextClassLoader(previousContextClassLoader); SPRING_BUILD_CONTEXT.set(null); } } /** * Wraps the bean to assure only that only methods of the published service * can be accessed. */ private Object shieldJavaService(Class serviceInterface, Object bean, Module module, ClassLoader classLoader) { JavaServiceShield handler = new JavaServiceShield(bean, module, serviceInterface, classLoader); return Proxy.newProxyInstance(classLoader, new Class[] { serviceInterface }, handler); } }