You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jg...@apache.org on 2015/04/15 11:00:36 UTC

tomee git commit: TOMEE-1547 allow resources to be loaded more lazily, support @PostConstruct and @PreDestroy in resources

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 <jo...@jrg.me.uk>
Authored: Wed Apr 15 09:59:19 2015 +0100
Committer: Jonathan Gallimore <jo...@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"));
+    }
+
+}