Repository: tomee Updated Branches: refs/heads/tomee-1.7.x 9da3b458e -> d4aec8fae
TOMEE-1547 allow resources to be loaded more lazily, support @PostConstruct and @PreDestroy in resources Project: http://git-wip-us.apache.org/repos/asf/tomee/repo Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/d4aec8fa Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/d4aec8fa Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/d4aec8fa Branch: refs/heads/tomee-1.7.x Commit: d4aec8fae0d1e618e23e1f3e19a5151a15fcce0d Parents: 9da3b45 Author: Jonathan Gallimore <j...@jrg.me.uk> Authored: Wed Apr 15 09:59:19 2015 +0100 Committer: Jonathan Gallimore <j...@jrg.me.uk> Committed: Wed Apr 15 09:59:19 2015 +0100 ---------------------------------------------------------------------- .../arquillian/tests/resource/ResourceTest.java | 117 +++++++++++ .../src/test/resources/META-INF/resources.xml | 37 ++++ .../openejb/assembler/classic/Assembler.java | 204 ++++++++++++++++--- .../apache/openejb/config/DeploymentLoader.java | 2 + .../apache/openejb/config/ReadDescriptors.java | 39 +++- .../META-INF/org.apache.openejb/service-jar.xml | 1 - .../assembler/classic/LazyResourceTest.java | 82 ++++++++ .../openejb/config/ReadDescriptorsTest.java | 115 +++++++++++ 8 files changed, 567 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tomee/blob/d4aec8fa/arquillian/arquillian-tomee-tests/arquillian-tomee-config-tests/src/test/java/org/apache/openejb/arquillian/tests/resource/ResourceTest.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-config-tests/src/test/java/org/apache/openejb/arquillian/tests/resource/ResourceTest.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-config-tests/src/test/java/org/apache/openejb/arquillian/tests/resource/ResourceTest.java new file mode 100644 index 0000000..dabc13b --- /dev/null +++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-config-tests/src/test/java/org/apache/openejb/arquillian/tests/resource/ResourceTest.java @@ -0,0 +1,117 @@ +/* + * 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.openejb.arquillian.tests.resource; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.annotation.Resource; +import javax.ejb.EJB; +import javax.ejb.Lock; +import javax.ejb.LockType; +import javax.ejb.Singleton; + +import org.apache.openejb.api.resource.DestroyableResource; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.EnterpriseArchive; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(Arquillian.class) +public class ResourceTest { + + @EJB + private TestEjb ejb; + + @Deployment + public static EnterpriseArchive createDeployment() { + + final JavaArchive ejbJar = ShrinkWrap.create(JavaArchive.class, "test-ejb.jar") + .addAsResource("META-INF/resources.xml", "META-INF/resources.xml") + .addClass(ResourceTest.class) + .addClass(Destroyable.class) + .addClass(Hello.class) + .addClass(TestEjb.class); + + final EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive.class, "test.ear") + .addAsModule(ejbJar); + + return ear; + } + + @Test + public void test() throws Exception { + Assert.assertTrue(ejb.isPostConstructCalled()); + } + + @Singleton + @Lock(LockType.READ) + public static class TestEjb { + + @Resource(name = "test/Hello") + private Hello hello; + + public boolean isPostConstructCalled() { + return hello.isPostConstructCalled(); + } + } + + public static class Hello { + + private boolean postConstructCalled = false; + private boolean preDestroyCalled = false; + + @PostConstruct + public void postConstruct() { + postConstructCalled = true; + } + + @PreDestroy + public void preDestroy() { + preDestroyCalled = true; + } + + public boolean isPostConstructCalled() { + return postConstructCalled; + } + + public boolean isPreDestroyCalled() { + return preDestroyCalled; + } + } + + public static class Destroyable implements DestroyableResource { + + private boolean destroyCalled = false; + + @Override + public void destroyResource() { + destroyCalled = true; + } + + public boolean isDestroyCalled() { + return destroyCalled; + } + } + +} http://git-wip-us.apache.org/repos/asf/tomee/blob/d4aec8fa/arquillian/arquillian-tomee-tests/arquillian-tomee-config-tests/src/test/resources/META-INF/resources.xml ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-config-tests/src/test/resources/META-INF/resources.xml b/arquillian/arquillian-tomee-tests/arquillian-tomee-config-tests/src/test/resources/META-INF/resources.xml new file mode 100644 index 0000000..0249b91 --- /dev/null +++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-config-tests/src/test/resources/META-INF/resources.xml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<!-- + ~ 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. + --> + +<Resources> + <Resource id="Hello" class-name="org.apache.openejb.arquillian.tests.resource.ResourceTest$Hello"> + code org.apache.openejb.arquillian.tests.resource.ResourceTest.Hello + Lazy true + UseAppClassLoader true + InitializeAfterDeployment true + </Resource> + + <Resource id="Destroyable" class-name="org.apache.openejb.arquillian.tests.resource.ResourceTest$Destroyable"> + code org.apache.openejb.arquillian.tests.resource.ResourceTest.Destroyable + Lazy true + UseAppClassLoader true + InitializeAfterDeployment true + </Resource> + +</Resources> + http://git-wip-us.apache.org/repos/asf/tomee/blob/d4aec8fa/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java index 9791e3b..0667bd3 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/Assembler.java @@ -39,6 +39,7 @@ import org.apache.openejb.NoSuchApplicationException; import org.apache.openejb.OpenEJBException; import org.apache.openejb.OpenEJBRuntimeException; import org.apache.openejb.UndeployException; +import org.apache.openejb.api.DestroyableResource; import org.apache.openejb.assembler.classic.event.AssemblerAfterApplicationCreated; import org.apache.openejb.assembler.classic.event.AssemblerBeforeApplicationDestroyed; import org.apache.openejb.assembler.classic.event.AssemblerCreated; @@ -64,6 +65,7 @@ import org.apache.openejb.config.ConfigurationFactory; import org.apache.openejb.config.NewLoaderLogic; import org.apache.openejb.config.QuickJarsTxtParser; import org.apache.openejb.config.TldScanner; +import org.apache.openejb.config.sys.Resource; import org.apache.openejb.core.ConnectorReference; import org.apache.openejb.core.CoreContainerSystem; import org.apache.openejb.core.CoreUserTransaction; @@ -77,6 +79,8 @@ import org.apache.openejb.core.ivm.IntraVmProxy; import org.apache.openejb.core.ivm.naming.ContextualJndiReference; import org.apache.openejb.core.ivm.naming.IvmContext; import org.apache.openejb.core.ivm.naming.IvmJndiFactory; +import org.apache.openejb.core.ivm.naming.JndiUrlReference; +import org.apache.openejb.core.ivm.naming.LazyObjectReference; import org.apache.openejb.core.ivm.naming.Reference; import org.apache.openejb.core.security.SecurityContextHandler; import org.apache.openejb.core.timer.EjbTimerServiceImpl; @@ -130,7 +134,9 @@ import org.apache.openejb.util.classloader.ClassLoaderAwareHandler; import org.apache.openejb.util.classloader.URLClassLoaderFirst; import org.apache.openejb.util.proxy.ProxyFactory; import org.apache.openejb.util.proxy.ProxyManager; +import org.apache.webbeans.component.ResourceBean; import org.apache.webbeans.config.WebBeansContext; +import org.apache.webbeans.inject.OWBInjector; import org.apache.webbeans.logger.JULLoggerFactory; import org.apache.webbeans.spi.ContainerLifecycle; import org.apache.webbeans.spi.ContextsService; @@ -147,6 +153,8 @@ import org.apache.xbean.recipe.ObjectRecipe; import org.apache.xbean.recipe.Option; import org.apache.xbean.recipe.UnsetPropertiesRecipe; +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; import javax.enterprise.context.Dependent; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.spi.Bean; @@ -177,6 +185,7 @@ import javax.transaction.TransactionSynchronizationRegistry; import javax.validation.ValidationException; import javax.validation.Validator; import javax.validation.ValidatorFactory; + import java.io.ByteArrayInputStream; import java.io.Externalizable; import java.io.File; @@ -185,9 +194,11 @@ import java.io.InputStream; import java.io.InvalidObjectException; import java.io.ObjectStreamException; import java.io.Serializable; +import java.lang.annotation.Annotation; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.MalformedURLException; @@ -208,6 +219,7 @@ import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.TreeMap; +import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -644,9 +656,9 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A } Extensions.addExtensions(classLoader, appInfo.eventClassesNeedingAppClassloader); - logger.info("createApplication.start", appInfo.path); - + final Context containerSystemContext = containerSystem.getJNDIContext(); + // To start out, ensure we don't already have any beans deployed with duplicate IDs. This // is a conflict we can't handle. final List<String> used = new ArrayList<String>(); @@ -700,8 +712,6 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A appContext.set(AsynchronousPool.class, AsynchronousPool.create(appContext)); - final Context containerSystemContext = containerSystem.getJNDIContext(); - final Map<String, LazyValidatorFactory> lazyValidatorFactories = new HashMap<String, LazyValidatorFactory>(); final Map<String, LazyValidator> lazyValidators = new HashMap<String, LazyValidator>(); final boolean isGeronimo = SystemInstance.get().hasProperty("openejb.geronimo"); @@ -928,6 +938,8 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A } } + postConstructResources(appInfo, classLoader, containerSystemContext, appContext); + deployedApplications.put(appInfo.path, appInfo); resumePersistentSchedulers(appContext); @@ -947,6 +959,66 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A } } + private void postConstructResources(final AppInfo appInfo, final ClassLoader classLoader, final Context containerSystemContext, final AppContext appContext) throws NamingException, OpenEJBException { + final ClassLoader oldCl = Thread.currentThread().getContextClassLoader(); + + try { + Thread.currentThread().setContextClassLoader(classLoader); + + final Set<String> resourceIds = new HashSet<String>(appInfo.resourceIds); + final List<ResourceInfo> resourceList = config.facilities.resources; + + for (ResourceInfo resourceInfo : resourceList) { + if (!resourceIds.contains(resourceInfo.id)) { + continue; + } + + try { + final Class<?> cls = Class.forName(resourceInfo.className, true, classLoader); + final Method postConstruct = findMethodAnnotatedWith(PostConstruct.class, cls); + boolean initialize = "true".equalsIgnoreCase(String.valueOf(resourceInfo.properties.remove("InitializeAfterDeployment"))); + if (postConstruct != null || initialize) { + + Object resource = containerSystemContext.lookup(OPENEJB_RESOURCE_JNDI_PREFIX + resourceInfo.id); + if (resource instanceof LazyResource) { + resource = LazyResource.class.cast(resource).getObject(); + } + + try { + // wire up CDI + OWBInjector.inject(appContext.getBeanManager(), + resource, + appContext.getBeanManager().createCreationalContext(null)); + + if (postConstruct != null) { + postConstruct.invoke(resource); + } + } catch (Exception e) { + logger.fatal("Error calling @PostConstruct method on " + resource.getClass().getName()); + throw new OpenEJBException(e); + } + } + } catch (Exception e) { + logger.fatal("Error calling @PostConstruct method on " + resourceInfo.id); + throw new OpenEJBException(e); + } + } + } finally { + Thread.currentThread().setContextClassLoader(oldCl); + } + } + + private Method findMethodAnnotatedWith(final Class<? extends Annotation> annotation, final Class<?> cls) { + final Method[] methods = cls.getDeclaredMethods(); + for (final Method method : methods) { + if (method.getAnnotation(annotation) != null) { + return method; + } + } + + return null; + } + public static void mergeServices(final AppInfo appInfo) throws URISyntaxException { for (final ServiceInfo si : appInfo.services) { // used lazily by JaxWsServiceObjectFactory, we could do the same for resources if (!appInfo.properties.containsKey(si.id)) { @@ -1588,6 +1660,15 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A } catch (final Exception e) { logger.debug("Not processing resource on destroy: " + className, e); } + } else if (DestroyableResource.class.isInstance(object)) { + try { + DestroyableResource.class.cast(object).destroyResource(); + } catch (final RuntimeException e) { + logger.error(e.getMessage(), e); + } + } else if (hasPreDestroy(object)) { + logger.debug("Calling @PreDestroy on: " + className); + preDestroy(object); } else if (logger.isDebugEnabled() && !DataSource.class.isInstance(object)) { logger.debug("Not processing resource on destroy: " + className); } @@ -1608,6 +1689,31 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A } } + private void preDestroy(Object object) { + final Method preDestroy = findMethodAnnotatedWith(PreDestroy.class, object.getClass()); + if (preDestroy != null) { + try { + preDestroy.invoke(object); + } catch (Exception e) { + logger.error("Error when calling @PreDestroy", e); + } + } + } + + private boolean hasPreDestroy(final Object object) { + try { + Object resource = object; + if (resource instanceof LazyResource) { + resource = LazyResource.class.cast(resource).getObject(); + } + + Class<? extends Object> cls = resource.getClass(); + return findMethodAnnotatedWith(PreDestroy.class, cls) != null; + } catch (Exception e) { + return false; + } + } + public void destroyApplication(final String filePath) throws UndeployException, NoSuchApplicationException { final ReentrantLock l = lock; @@ -2281,6 +2387,57 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A } public void createResource(final ResourceInfo serviceInfo) throws OpenEJBException { + Object service = "true".equalsIgnoreCase(String.valueOf(serviceInfo.properties.remove("Lazy"))) ? + newLazyResource(serviceInfo) : + doCreateResource(serviceInfo); + + bindResource(serviceInfo.id, service); + for (final String alias : serviceInfo.aliases) { + bindResource(alias, service); + } + if (serviceInfo.originAppName != null && !serviceInfo.originAppName.isEmpty() && !"/".equals(serviceInfo.originAppName) + && !serviceInfo.id.startsWith("global")) { + final String baseJndiName = serviceInfo.id.substring(serviceInfo.originAppName.length() + 1); + serviceInfo.aliases.add(baseJndiName); + final ContextualJndiReference ref = new ContextualJndiReference(baseJndiName); + ref.addPrefix(serviceInfo.originAppName); + bindResource(baseJndiName, ref); + } + + // Update the config tree + config.facilities.resources.add(serviceInfo); + + if (logger.isDebugEnabled()) { // weird to check parent logger but save time and it is almost never activated + logger.getChildLogger("service").debug("createService.success", serviceInfo.service, serviceInfo.id, serviceInfo.className); + } + } + + private LazyResource newLazyResource(final ResourceInfo serviceInfo) { + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + return new LazyResource(new Callable<Object>() { + @Override + public Object call() throws Exception { + final boolean appClassLoader = "true".equals(serviceInfo.properties.remove("UseAppClassLoader")); + + ClassLoader old = null; + + if (!appClassLoader) { + old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(loader); + } + + try { + return doCreateResource(serviceInfo); + } finally { + if (old != null) { + Thread.currentThread().setContextClassLoader(old); + } + } + } + }); + } + + private Object doCreateResource(final ResourceInfo serviceInfo) throws OpenEJBException { final ObjectRecipe serviceRecipe = createRecipe(serviceInfo); final boolean properties = PropertiesFactory.class.getName().equals(serviceInfo.className); if ("false".equalsIgnoreCase(serviceInfo.properties.getProperty("SkipImplicitAttributes", "false")) && !properties) { @@ -2297,7 +2454,7 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A final InputStream is = new ByteArrayInputStream(serviceInfo.properties.getProperty("Definition").getBytes()); final Properties p = new SuperProperties(); IO.readProperties(is, p); - for (final Map.Entry<Object, Object> entry : p.entrySet()) { + for (final Entry<Object, Object> entry : p.entrySet()) { final String key = entry.getKey().toString(); if (!props.containsKey(key) // never override from Definition, just use it to complete the properties set @@ -2440,7 +2597,7 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A final Map<String, Object> unsetA = serviceRecipe.getUnsetProperties(); final Map<String, Object> unsetB = connectionManagerRecipe.getUnsetProperties(); final Map<String, Object> unset = new HashMap<String, Object>(); - for (final Map.Entry<String, Object> entry : unsetA.entrySet()) { + for (final Entry<String, Object> entry : unsetA.entrySet()) { if (unsetB.containsKey(entry.getKey())) { unset.put(entry.getKey(), entry.getValue()); } @@ -2504,26 +2661,7 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A } else if (!Properties.class.isInstance(service)) { logUnusedProperties(serviceRecipe, serviceInfo); } - - bindResource(serviceInfo.id, service); - for (final String alias : serviceInfo.aliases) { - bindResource(alias, service); - } - if (serviceInfo.originAppName != null && !serviceInfo.originAppName.isEmpty() && !"/".equals(serviceInfo.originAppName) - && !serviceInfo.id.startsWith("global")) { - final String baseJndiName = serviceInfo.id.substring(serviceInfo.originAppName.length() + 1); - serviceInfo.aliases.add(baseJndiName); - final ContextualJndiReference ref = new ContextualJndiReference(baseJndiName); - ref.addPrefix(serviceInfo.originAppName); - bindResource(baseJndiName, ref); - } - - // Update the config tree - config.facilities.resources.add(serviceInfo); - - if (logger.isDebugEnabled()) { // weird to check parent logger but save time and it is almost never activated - logger.getChildLogger("service").debug("createService.success", serviceInfo.service, serviceInfo.id, serviceInfo.className); - } + return service; } private void bindResource(final String id, final Object service) throws OpenEJBException { @@ -2977,4 +3115,18 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A } } } + + public static class LazyResource extends LazyObjectReference<Object> { + public LazyResource(final Callable<Object> creator) { + super(creator); + } + + Object writeReplace() throws ObjectStreamException { + try { + return getObject(); + } catch (final NamingException e) { + return null; + } + } + } } http://git-wip-us.apache.org/repos/asf/tomee/blob/d4aec8fa/container/openejb-core/src/main/java/org/apache/openejb/config/DeploymentLoader.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/DeploymentLoader.java b/container/openejb-core/src/main/java/org/apache/openejb/config/DeploymentLoader.java index 0a7514b..21c41bc 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/config/DeploymentLoader.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/config/DeploymentLoader.java @@ -25,6 +25,8 @@ import org.apache.openejb.api.RemoteClient; import org.apache.openejb.classloader.ClassLoaderConfigurer; import org.apache.openejb.classloader.WebAppEnricher; import org.apache.openejb.config.event.BeforeDeploymentEvent; +import org.apache.openejb.config.sys.Resource; +import org.apache.openejb.config.sys.Resources; import org.apache.openejb.core.EmptyResourcesClassLoader; import org.apache.openejb.core.ParentClassLoaderFinder; import org.apache.openejb.jee.Application; http://git-wip-us.apache.org/repos/asf/tomee/blob/d4aec8fa/container/openejb-core/src/main/java/org/apache/openejb/config/ReadDescriptors.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/ReadDescriptors.java b/container/openejb-core/src/main/java/org/apache/openejb/config/ReadDescriptors.java index 0c1f835..5faeb6f 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/config/ReadDescriptors.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/config/ReadDescriptors.java @@ -20,7 +20,9 @@ package org.apache.openejb.config; import org.apache.openejb.OpenEJBException; import org.apache.openejb.config.sys.JSonConfigReader; import org.apache.openejb.config.sys.JaxbOpenejb; +import org.apache.openejb.config.sys.Resource; import org.apache.openejb.config.sys.Resources; +import org.apache.openejb.core.ParentClassLoaderFinder; import org.apache.openejb.jee.ApplicationClient; import org.apache.openejb.jee.Beans; import org.apache.openejb.jee.Connector; @@ -72,6 +74,7 @@ import javax.xml.bind.JAXBException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; @@ -241,7 +244,6 @@ public class ReadDescriptors implements DynamicDeployer { } return appModule; - } public static void readResourcesXml(final Module module) { @@ -250,7 +252,7 @@ public class ReadDescriptors implements DynamicDeployer { if (url != null) { try { final Resources openejb = JaxbOpenejb.unmarshal(Resources.class, url.get()); - module.initResources(openejb); + module.initResources(check(openejb)); } catch (final Exception e) { logger.warning("can't read " + url.toString() + " to load resources for module " + module.toString(), e); } @@ -261,7 +263,7 @@ public class ReadDescriptors implements DynamicDeployer { if (url != null) { try { final Resources openejb = JSonConfigReader.read(Resources.class, url.get()); - module.initResources(openejb); + module.initResources(check(openejb)); } catch (final Exception e) { logger.warning("can't read " + url.toString() + " to load resources for module " + module.toString(), e); } @@ -269,6 +271,37 @@ public class ReadDescriptors implements DynamicDeployer { } } + public static Resources check(final Resources resources) { + final List<Resource> resourceList = resources.getResource(); + for (final Resource resource : resourceList) { + if (resource.getClassName() != null) { + try { + ParentClassLoaderFinder.Helper.get().loadClass(resource.getClassName()); + if (resource.getType() != null) { + ParentClassLoaderFinder.Helper.get().loadClass(resource.getType()); + } + continue; + } catch (Exception e) { + } + + // if the resource class cannot be loaded, + // set the lazy property to true + // and the app classloader property to true + + final Boolean lazySpecified = Boolean.valueOf(resource.getProperties().getProperty("Lazy", "false")); + + resource.getProperties().setProperty("Lazy", "true"); + resource.getProperties().setProperty("UseAppClassLoader", "true"); + + if (!lazySpecified) { + resource.getProperties().setProperty("InitializeAfterDeployment", "true"); + } + } + } + + return resources; + } + private void readValidationConfigType(final Module module) throws OpenEJBException { if (module.getValidationConfig() != null) { return; http://git-wip-us.apache.org/repos/asf/tomee/blob/d4aec8fa/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml b/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml index 89669ba..e366823 100644 --- a/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml +++ b/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml @@ -1142,5 +1142,4 @@ service="Resource" types="ContextService, javax.enterprise.concurrent.ContextService" class-name="org.apache.openejb.concurrencyutilities.ee.impl.ContextServiceImpl"/> - </ServiceJar> http://git-wip-us.apache.org/repos/asf/tomee/blob/d4aec8fa/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/LazyResourceTest.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/LazyResourceTest.java b/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/LazyResourceTest.java new file mode 100644 index 0000000..1b19f1c --- /dev/null +++ b/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/LazyResourceTest.java @@ -0,0 +1,82 @@ +/* + * 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.openejb.assembler.classic; + +import org.apache.openejb.config.AppModule; +import org.apache.openejb.config.EjbModule; +import org.apache.openejb.jee.EjbJar; +import org.apache.openejb.junit.ApplicationComposer; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.spi.ContainerSystem; +import org.apache.openejb.testing.Configuration; +import org.apache.openejb.testing.Module; +import org.apache.openejb.testng.PropertiesBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.naming.NamingException; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(ApplicationComposer.class) +public class LazyResourceTest { + @Test + public void lazy() throws NamingException { + assertEquals(1, MyResource1.count); + assertEquals(0, MyResource2.count); + assertTrue(MyResource2.class.isInstance( + SystemInstance.get().getComponent(ContainerSystem.class).getJNDIContext().lookup("openejb/Resource/r2"))); + assertEquals(1, MyResource2.count); + } + + @Module + public AppModule application() { + final EjbModule ejbModule = new EjbModule(new EjbJar()); + + final AppModule appModule = new AppModule(Thread.currentThread().getContextClassLoader(), null); + appModule.getEjbModules().add(ejbModule); + + return appModule; + } + + @Configuration + public Properties config() { + return new PropertiesBuilder() + .p("r1", "new://Resource?class-name=org.apache.openejb.assembler.classic.LazyResourceTest$MyResource1") + .p("r2", "new://Resource?class-name=org.apache.openejb.assembler.classic.LazyResourceTest$MyResource2") + .p("r2.Lazy", "true") + .build(); + } + + public static class MyResource1 { + public static volatile int count = 0; + + public MyResource1() { + count++; + } + } + + public static class MyResource2 { + public static volatile int count = 0; + + public MyResource2() { + count++; + } + } +} http://git-wip-us.apache.org/repos/asf/tomee/blob/d4aec8fa/container/openejb-core/src/test/java/org/apache/openejb/config/ReadDescriptorsTest.java ---------------------------------------------------------------------- diff --git a/container/openejb-core/src/test/java/org/apache/openejb/config/ReadDescriptorsTest.java b/container/openejb-core/src/test/java/org/apache/openejb/config/ReadDescriptorsTest.java new file mode 100644 index 0000000..7413109 --- /dev/null +++ b/container/openejb-core/src/test/java/org/apache/openejb/config/ReadDescriptorsTest.java @@ -0,0 +1,115 @@ +/* + * 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.openejb.config; + + +import org.apache.openejb.config.sys.Resource; +import org.apache.openejb.config.sys.Resources; +import org.junit.Assert; +import org.junit.Test; + +public class ReadDescriptorsTest { + + @Test + public void testClassNotAvailable() { + + final Resource resource = new Resource(); + resource.setClassName("not.a.real.Class"); + + final Resources resources = new Resources(); + resources.add(resource); + + final Resources checkedResources = ReadDescriptors.check(resources); + final Resource res = checkedResources.getResource().get(0); + + Assert.assertEquals("true", res.getProperties().getProperty("Lazy")); + Assert.assertEquals("true", res.getProperties().getProperty("UseAppClassLoader")); + Assert.assertEquals("true", res.getProperties().getProperty("InitializeAfterDeployment")); + } + + @Test + public void testTypeNotAvailable() { + + final Resource resource = new Resource(); + resource.setClassName("org.apache.openejb.config.ReadDescriptorsTest"); + resource.setType("not.a.real.Class"); + + final Resources resources = new Resources(); + resources.add(resource); + + final Resources checkedResources = ReadDescriptors.check(resources); + final Resource res = checkedResources.getResource().get(0); + + Assert.assertEquals("true", res.getProperties().getProperty("Lazy")); + Assert.assertEquals("true", res.getProperties().getProperty("UseAppClassLoader")); + Assert.assertEquals("true", res.getProperties().getProperty("InitializeAfterDeployment")); + } + + @Test + public void testClassAndTypeAvailable() { + + final Resource resource = new Resource(); + resource.setClassName("org.apache.openejb.config.ReadDescriptorsTest"); + resource.setType("org.apache.openejb.config.ReadDescriptorsTest"); + + final Resources resources = new Resources(); + resources.add(resource); + + final Resources checkedResources = ReadDescriptors.check(resources); + final Resource res = checkedResources.getResource().get(0); + + Assert.assertNull(res.getProperties().getProperty("Lazy")); + Assert.assertNull(res.getProperties().getProperty("UseAppClassLoader")); + Assert.assertNull(res.getProperties().getProperty("InitializeAfterDeployment")); + } + + @Test + public void testClassAvailable() { + + final Resource resource = new Resource(); + resource.setClassName("org.apache.openejb.config.ReadDescriptorsTest"); + + final Resources resources = new Resources(); + resources.add(resource); + + final Resources checkedResources = ReadDescriptors.check(resources); + final Resource res = checkedResources.getResource().get(0); + + Assert.assertNull(res.getProperties().getProperty("Lazy")); + Assert.assertNull(res.getProperties().getProperty("UseAppClassLoader")); + Assert.assertNull(res.getProperties().getProperty("InitializeAfterDeployment")); + } + + @Test + public void testLazyResource() { + final Resource resource = new Resource(); + resource.setClassName("not.a.real.Class"); + resource.setType("not.a.real.Class"); + resource.getProperties().setProperty("Lazy", "true"); + + final Resources resources = new Resources(); + resources.add(resource); + + final Resources checkedResources = ReadDescriptors.check(resources); + final Resource res = checkedResources.getResource().get(0); + + Assert.assertEquals("true", res.getProperties().getProperty("Lazy")); + Assert.assertEquals("true", res.getProperties().getProperty("UseAppClassLoader")); + Assert.assertNull(res.getProperties().getProperty("InitializeAfterDeployment")); + } + +}