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

tomee git commit: TOMEE-1548 @PostContruct/@preDestroy support in resource attributes - with merge

Repository: tomee
Updated Branches:
  refs/heads/master 02ff59e1a -> 2a29aef9f


TOMEE-1548 @PostContruct/@preDestroy support in resource attributes - with merge


Project: http://git-wip-us.apache.org/repos/asf/tomee/repo
Commit: http://git-wip-us.apache.org/repos/asf/tomee/commit/2a29aef9
Tree: http://git-wip-us.apache.org/repos/asf/tomee/tree/2a29aef9
Diff: http://git-wip-us.apache.org/repos/asf/tomee/diff/2a29aef9

Branch: refs/heads/master
Commit: 2a29aef9ffaf829fdc9e950337fea49eb1a10862
Parents: 02ff59e
Author: Romain Manni-Bucau <rm...@apache.org>
Authored: Wed Apr 15 20:14:07 2015 +0200
Committer: Romain Manni-Bucau <rm...@apache.org>
Committed: Wed Apr 15 20:14:07 2015 +0200

----------------------------------------------------------------------
 .../src/test/resources/META-INF/resources.xml   |   2 -
 .../openejb/assembler/classic/Assembler.java    | 205 ++++++++++++------
 .../openejb/assembler/classic/ResourceInfo.java |   2 +
 .../openejb/config/ConfigurationFactory.java    |  13 +-
 .../org/apache/openejb/config/sys/Resource.java |  22 ++
 .../apache/openejb/config/sys/StackHandler.java |   2 +
 .../ivm/naming/ContextualJndiReference.java     |   4 +
 .../openejb/resource/ResourceLifecycleTest.java | 208 +++++++++++++++++++
 8 files changed, 394 insertions(+), 64 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/2a29aef9/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
index 0249b91..3edb7dc 100644
--- 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
@@ -20,14 +20,12 @@
 
 <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

http://git-wip-us.apache.org/repos/asf/tomee/blob/2a29aef9/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 156e6c0..0a98318 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
@@ -129,6 +129,7 @@ import org.apache.openejb.util.PropertiesHelper;
 import org.apache.openejb.util.PropertyPlaceHolderHelper;
 import org.apache.openejb.util.References;
 import org.apache.openejb.util.SafeToolkit;
+import org.apache.openejb.util.SetAccessible;
 import org.apache.openejb.util.SuperProperties;
 import org.apache.openejb.util.URISupport;
 import org.apache.openejb.util.URLs;
@@ -138,6 +139,7 @@ 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.container.BeanManagerImpl;
 import org.apache.webbeans.inject.OWBInjector;
 import org.apache.webbeans.logger.JULLoggerFactory;
 import org.apache.webbeans.spi.BeanArchiveService;
@@ -150,13 +152,19 @@ import org.apache.webbeans.spi.ScannerService;
 import org.apache.webbeans.spi.TransactionService;
 import org.apache.webbeans.spi.adaptor.ELAdaptor;
 import org.apache.webbeans.spi.api.ResourceReference;
+import org.apache.xbean.finder.AnnotationFinder;
 import org.apache.xbean.finder.ClassLoaders;
 import org.apache.xbean.finder.ResourceFinder;
 import org.apache.xbean.finder.UrlSet;
+import org.apache.xbean.finder.archive.ClassesArchive;
 import org.apache.xbean.recipe.ObjectRecipe;
 import org.apache.xbean.recipe.Option;
 import org.apache.xbean.recipe.UnsetPropertiesRecipe;
 
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.Collection;
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 import javax.annotation.Resource;
@@ -203,7 +211,6 @@ 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;
@@ -234,6 +241,8 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.ReentrantLock;
 
+import static org.apache.openejb.util.Classes.ancestors;
+
 @SuppressWarnings({"UnusedDeclaration", "UnqualifiedFieldAccess", "UnqualifiedMethodAccess"})
 public class Assembler extends AssemblerTool implements org.apache.openejb.spi.Assembler, JndiConstants {
 
@@ -539,9 +548,11 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
 
         createSecurityService(configInfo.facilities.securityService);
 
+        final Set<String> rIds = new HashSet<>(configInfo.facilities.resources.size());
         for (final ResourceInfo resourceInfo : configInfo.facilities.resources) {
             createResource(resourceInfo);
         }
+        postConstructResources(rIds, ParentClassLoaderFinder.Helper.get(), systemInstance.getComponent(ContainerSystem.class).getJNDIContext(), null);
 
         // Containers
         for (final ContainerInfo serviceInfo : containerSystemInfo.containers) {
@@ -980,7 +991,7 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
                 }
             }
 
-            postConstructResources(appInfo, classLoader, containerSystemContext, appContext);
+            postConstructResources(appInfo.resourceIds, classLoader, containerSystemContext, appContext);
             
             deployedApplications.put(appInfo.path, appInfo);
             resumePersistentSchedulers(appContext);
@@ -1096,67 +1107,89 @@ 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();
+    private void postConstructResources(final Set<String> inResourceIds, final ClassLoader classLoader, final Context containerSystemContext, final AppContext appContext) throws NamingException, OpenEJBException {
+        final Thread thread = Thread.currentThread();
+        final ClassLoader oldCl = thread.getContextClassLoader();
 
         try {
-            Thread.currentThread().setContextClassLoader(classLoader);
+            thread.setContextClassLoader(classLoader);
 
-            final Set<String> resourceIds = new HashSet<String>(appInfo.resourceIds);
+            final Set<String> resourceIds = new HashSet<>(inResourceIds);
             final List<ResourceInfo> resourceList = config.facilities.resources;
 
             for (final 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);
+                    final Class<?> clazz = classLoader.loadClass(resourceInfo.className);
+                    final AnnotationFinder finder = new AnnotationFinder(new ClassesArchive(ancestors(clazz)));
+                    final List<Method> postConstructs = finder.findAnnotatedMethods(PostConstruct.class);
+                    final List<Method> preDestroys = finder.findAnnotatedMethods(PreDestroy.class);
                     final 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();
-                            this.bindResource(resourceInfo.id, resource);
+                    CreationalContext<?> creationalContext = null;
+                    Object originalResource = null;
+                    if (!postConstructs.isEmpty() || initialize) {
+                        originalResource = containerSystemContext.lookup(OPENEJB_RESOURCE_JNDI_PREFIX + resourceInfo.id);
+                        Object resource = originalResource;
+                        if (resource instanceof Reference) {
+                            resource = unwrapReference(resource);
+                            this.bindResource(resourceInfo.id, resource, true);
                         }
 
                         try {
                             // wire up CDI
-                            OWBInjector.inject(appContext.getBeanManager(),
-                                    resource,
-                                    appContext.getBeanManager().createCreationalContext(null));
+                            if (appContext != null) {
+                                final BeanManagerImpl beanManager = appContext.getWebBeansContext().getBeanManagerImpl();
+                                if (beanManager.isInUse()) {
+                                    creationalContext = beanManager.createCreationalContext(null);
+                                    OWBInjector.inject(beanManager, resource, creationalContext);
+                                }
+                            }
 
-                            if (postConstruct != null) {
-                                postConstruct.invoke(resource);
+                            if (resourceInfo.postConstruct != null) {
+                                final Method p = clazz.getDeclaredMethod(resourceInfo.postConstruct);
+                                if (!p.isAccessible()) {
+                                    SetAccessible.on(p);
+                                }
+                                p.invoke(resource);
+                            }
+
+                            for (final Method m : postConstructs) {
+                                if (!m.isAccessible()) {
+                                    SetAccessible.on(m);
+                                }
+                                m.invoke(resource);
                             }
-                        } catch (Exception e) {
+                        } catch (final Exception e) {
                             logger.fatal("Error calling @PostConstruct method on " + resource.getClass().getName());
                             throw new OpenEJBException(e);
                         }
                     }
-                } catch (Exception e) {
+
+                    if (resourceInfo.preDestroy != null) {
+                        final Method p = clazz.getDeclaredMethod(resourceInfo.preDestroy);
+                        if (!p.isAccessible()) {
+                            SetAccessible.on(p);
+                        }
+                        preDestroys.add(p);
+                    }
+
+                    if (!preDestroys.isEmpty() || creationalContext != null) {
+                        final String name = OPENEJB_RESOURCE_JNDI_PREFIX + resourceInfo.id;
+                        if (originalResource == null) {
+                            originalResource = containerSystemContext.lookup(name);
+                        }
+                        this.bindResource(resourceInfo.id, new ResourceInstance(name, originalResource, preDestroys, creationalContext), true);
+                    }
+                } catch (final Exception e) {
                     logger.fatal("Error calling @PostConstruct method on " + resourceInfo.id);
                     throw new OpenEJBException(e);
                 }
             }
         } finally {
-            Thread.currentThread().setContextClassLoader(oldCl);
+            thread.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 {
         // used lazily by JaxWsServiceObjectFactory so merge both to keep same config
         // note: we could do the same for resources
@@ -1761,7 +1794,7 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
 
     private void destroyResource(final String name, final String className, final Object object) {
 
-        Method preDestroy = null;
+        Collection<Method> preDestroy = null;
 
         if (object instanceof ResourceAdapterReference) {
             final ResourceAdapterReference resourceAdapter = (ResourceAdapterReference) object;
@@ -1821,13 +1854,6 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
             } catch (final RuntimeException e) {
                 logger.error(e.getMessage(), e);
             }
-        } else if ((preDestroy = findPreDestroy(object)) != null) {
-            logger.debug("Calling @PreDestroy on: " + className);
-            try {
-                preDestroy.invoke(object);
-            } catch (final Exception e) {
-                logger.error(e.getMessage(), e);
-            }
         } else if (logger.isDebugEnabled() && !DataSource.class.isInstance(object)) {
             logger.debug("Not processing resource on destroy: " + className);
         }
@@ -1848,18 +1874,19 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
         }
     }
 
-    private Method findPreDestroy(final Object object) {
-        try {
-            Object resource = object;
-            if (LazyResource.class.isInstance(resource) && LazyResource.class.cast(resource).isInitialized()) {
-                resource = LazyResource.class.cast(resource).getObject();
+    private static Object unwrapReference(final Object object) {
+        Object o = object;
+        while (o != null && Reference.class.isInstance(o)) {
+            try {
+                o = Reference.class.cast(o).getObject();
+            } catch (final NamingException e) {
+                // break
             }
-
-            final Class<? extends Object> cls = resource.getClass();
-            return findMethodAnnotatedWith(PreDestroy.class, cls);
-        } catch (Exception e) {
-            return null;
         }
+        if (o == null) {
+            o = object;
+        }
+        return o;
     }
 
     public void destroyApplication(final String filePath) throws UndeployException, NoSuchApplicationException {
@@ -2539,9 +2566,9 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
                 newLazyResource(serviceInfo) :
                 doCreateResource(serviceInfo);
 
-        bindResource(serviceInfo.id, service);
+        bindResource(serviceInfo.id, service, false);
         for (final String alias : serviceInfo.aliases) {
-            bindResource(alias, service);
+            bindResource(alias, service, false);
         }
         if (serviceInfo.originAppName != null && !serviceInfo.originAppName.isEmpty() && !"/".equals(serviceInfo.originAppName)
             && !serviceInfo.id.startsWith("global")) {
@@ -2549,7 +2576,7 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
             serviceInfo.aliases.add(baseJndiName);
             final ContextualJndiReference ref = new ContextualJndiReference(baseJndiName);
             ref.addPrefix(serviceInfo.originAppName);
-            bindResource(baseJndiName, ref);
+            bindResource(baseJndiName, ref, false);
         }
 
         // Update the config tree
@@ -2812,7 +2839,7 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
         return service;
     }
 
-    private void bindResource(final String id, final Object service) throws OpenEJBException {
+    private void bindResource(final String id, final Object service, final boolean canReplace) throws OpenEJBException {
         final String name = OPENEJB_RESOURCE_JNDI_PREFIX + id;
         final Context jndiContext = containerSystem.getJNDIContext();
         Object existing = null;
@@ -2835,12 +2862,21 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
             } else if (existingIsContextual && !serviceIsExisting) {
                 ContextualJndiReference.class.cast(existing).setDefaultValue(service);
             } else if (existingIsContextual) { // && serviceIsExisting is always true here
-                ContextualJndiReference.class.cast(existing).addPrefix(ContextualJndiReference.class.cast(service).lastPrefix());
+                final ContextualJndiReference contextual = ContextualJndiReference.class.cast(existing);
+                if (canReplace && contextual.prefixesSize() == 1) { // replace!
+                    contextual.removePrefix(contextual.lastPrefix());
+                    contextual.setDefaultValue(service);
+                } else {
+                    contextual.addPrefix(ContextualJndiReference.class.cast(service).lastPrefix());
+                }
                 return;
             }
         }
 
         try {
+            if (canReplace && existing != null) {
+                jndiContext.unbind(name);
+            }
             if (rebind) {
                 jndiContext.rebind(name, service);
             } else {
@@ -3280,4 +3316,55 @@ public class Assembler extends AssemblerTool implements org.apache.openejb.spi.A
             }
         }
     }
+
+    public static class ResourceInstance extends Reference implements Serializable, DestroyableResource {
+        private final String name;
+        private final Object delegate;
+        private transient final Collection<Method> preDestroys;
+        private transient final CreationalContext<?> context;
+
+        public ResourceInstance(final String name, final Object delegate, final Collection<Method> preDestroys, final CreationalContext<?> context) {
+            this.name = name;
+            this.delegate = delegate;
+            this.preDestroys = preDestroys;
+            this.context = context;
+        }
+
+        @Override
+        public Object getObject() throws NamingException {
+            return delegate;
+        }
+
+        @Override
+        public void destroyResource() {
+            final Object o = unwrapReference(delegate);
+            for (final Method m : preDestroys) {
+                try {
+                    if (!m.isAccessible()) {
+                        SetAccessible.on(m);
+                    }
+                    m.invoke(o);
+                } catch (final Exception e) {
+                    SystemInstance.get().getComponent(Assembler.class).logger.error(e.getMessage(), e);
+                }
+            }
+            try {
+                if (context != null) {
+                    context.release();
+                }
+            } catch (final Exception e) {
+                // no-op
+            }
+        }
+
+        // we don't care unwrapping the resource here since we want to keep ResourceInstance data for destruction
+        // which is never serialized (IvmContext)
+        Object readResolve() throws ObjectStreamException {
+            try {
+                return SystemInstance.get().getComponent(ContainerSystem.class).getJNDIContext().lookup(name);
+            } catch (final NamingException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/tomee/blob/2a29aef9/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ResourceInfo.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ResourceInfo.java b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ResourceInfo.java
index 502947e..c24ae24 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ResourceInfo.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ResourceInfo.java
@@ -22,6 +22,8 @@ import java.util.List;
 
 public class ResourceInfo extends ServiceInfo {
     public String jndiName = "";
+    public String postConstruct;
+    public String preDestroy;
     public String originAppName; // if define by an app
     public List<String> aliases = new ArrayList<String>();
 }

http://git-wip-us.apache.org/repos/asf/tomee/blob/2a29aef9/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java b/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java
index 075c76a..bfbbc84 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/config/ConfigurationFactory.java
@@ -773,10 +773,13 @@ public class ConfigurationFactory implements OpenEjbConfigurationFactory {
                 }
 
                 if (object instanceof Resource) {
+                    final Resource resource = Resource.class.cast(object);
                     final String aliases = map.remove("aliases");
                     if (aliases != null) {
-                        ((Resource) object).getAliases().addAll(Arrays.asList(aliases.split(",")));
+                        resource.getAliases().addAll(Arrays.asList(aliases.split(",")));
                     }
+                    resource.setPostConstruct(map.remove("post-construct"));
+                    resource.setPreDestroy(map.remove("pre-destroy"));
                 }
 
                 service.getProperties().putAll(map);
@@ -1230,8 +1233,12 @@ public class ConfigurationFactory implements OpenEjbConfigurationFactory {
             info.properties = props;
             info.constructorArgs.addAll(parseConstructorArgs(provider));
             if (info instanceof ResourceInfo && service instanceof Resource) {
-                ((ResourceInfo) info).jndiName = ((Resource) service).getJndi();
-                ((ResourceInfo) info).aliases.addAll(((Resource) service).getAliases());
+                final ResourceInfo ri = ResourceInfo.class.cast(info);
+                final Resource resource = Resource.class.cast(service);
+                ri.jndiName = resource.getJndi();
+                ri.postConstruct = resource.getPostConstruct();
+                ri.preDestroy = resource.getPreDestroy();
+                ri.aliases.addAll(resource.getAliases());
             }
 
             if (service.getClasspath() != null && service.getClasspath().length() > 0) {

http://git-wip-us.apache.org/repos/asf/tomee/blob/2a29aef9/container/openejb-core/src/main/java/org/apache/openejb/config/sys/Resource.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/sys/Resource.java b/container/openejb-core/src/main/java/org/apache/openejb/config/sys/Resource.java
index 2e651f6..f953be7 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/config/sys/Resource.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/config/sys/Resource.java
@@ -52,6 +52,12 @@ public class Resource extends AbstractService {
     @XmlAttribute
     protected String jndi;
 
+    @XmlAttribute(name = "post-construct")
+    protected String postConstruct;
+
+    @XmlAttribute(name = "pre-destroy")
+    protected String preDestroy;
+
     @XmlElement(name = "aliases")
     protected List<String> aliases = new ArrayList<String>();
 
@@ -94,6 +100,22 @@ public class Resource extends AbstractService {
         return aliases;
     }
 
+    public String getPostConstruct() {
+        return postConstruct;
+    }
+
+    public void setPostConstruct(final String postConstruct) {
+        this.postConstruct = postConstruct;
+    }
+
+    public String getPreDestroy() {
+        return preDestroy;
+    }
+
+    public void setPreDestroy(final String preDestroy) {
+        this.preDestroy = preDestroy;
+    }
+
     @Override
     public boolean equals(final Object o) {
         if (this == o) {

http://git-wip-us.apache.org/repos/asf/tomee/blob/2a29aef9/container/openejb-core/src/main/java/org/apache/openejb/config/sys/StackHandler.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/sys/StackHandler.java b/container/openejb-core/src/main/java/org/apache/openejb/config/sys/StackHandler.java
index 9e23128..77c0c49 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/config/sys/StackHandler.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/config/sys/StackHandler.java
@@ -222,6 +222,8 @@ public class StackHandler extends DefaultHandler {
         public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException {
             super.startElement(uri, localName, qName, attributes);
             service.setJndi(attributes.getValue("jndi"));
+            service.setPostConstruct(attributes.getValue("post-construct"));
+            service.setPreDestroy(attributes.getValue("pre-destroy"));
             service.setPropertiesProvider(attributes.getValue("property-provider"));
 
             final String aliases = attributes.getValue("aliases");

http://git-wip-us.apache.org/repos/asf/tomee/blob/2a29aef9/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/naming/ContextualJndiReference.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/naming/ContextualJndiReference.java b/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/naming/ContextualJndiReference.java
index f225b3f..cd7e947 100644
--- a/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/naming/ContextualJndiReference.java
+++ b/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/naming/ContextualJndiReference.java
@@ -73,6 +73,10 @@ public class ContextualJndiReference extends IntraVmJndiReference {
         return prefixes.isEmpty();
     }
 
+    public int prefixesSize() {
+        return prefixes.size();
+    }
+
     @Override
     public Object getObject() throws NamingException {
         final Boolean rawValue = !followReference.get();

http://git-wip-us.apache.org/repos/asf/tomee/blob/2a29aef9/container/openejb-core/src/test/java/org/apache/openejb/resource/ResourceLifecycleTest.java
----------------------------------------------------------------------
diff --git a/container/openejb-core/src/test/java/org/apache/openejb/resource/ResourceLifecycleTest.java b/container/openejb-core/src/test/java/org/apache/openejb/resource/ResourceLifecycleTest.java
new file mode 100644
index 0000000..8f10ad1
--- /dev/null
+++ b/container/openejb-core/src/test/java/org/apache/openejb/resource/ResourceLifecycleTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.resource;
+
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.testing.Classes;
+import org.apache.openejb.testing.ContainerProperties;
+import org.apache.openejb.testing.SimpleLog;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@Classes
+@SimpleLog
+@ContainerProperties({
+        @ContainerProperties.Property(name = "postConstructAndPreDestroy", value = "new://Resource?class-name=org.apache.openejb.resource.ResourceLifecycleTest$WithBoth"),
+        @ContainerProperties.Property(name = "postConstructAndPreDestroy.myAttr", value = "done"),
+        @ContainerProperties.Property(name = "postConstructOnly", value = "new://Resource?class-name=org.apache.openejb.resource.ResourceLifecycleTest$WithPostConstruct"),
+        @ContainerProperties.Property(name = "postConstructOnly.myAttr", value = "done"),
+        @ContainerProperties.Property(name = "postConstructAnnotationAndConfigOnly", value = "new://Resource?class-name=org.apache.openejb.resource.ResourceLifecycleTest$WithPostConstruct&post-construct=init2"),
+        @ContainerProperties.Property(name = "postConstructAnnotationAndConfigOnly.myAttr", value = "done"),
+        @ContainerProperties.Property(name = "preDestroyOnly", value = "new://Resource?class-name=org.apache.openejb.resource.ResourceLifecycleTest$WithPreDestroy"),
+        @ContainerProperties.Property(name = "preDestroyOnly.myAttr", value = "done"),
+        @ContainerProperties.Property(name = "preDestroyAnnotationAndConfigOnly", value = "new://Resource?class-name=org.apache.openejb.resource.ResourceLifecycleTest$WithPreDestroy&pre-destroy=init2"),
+        @ContainerProperties.Property(name = "preDestroyAnnotationAndConfigOnly.myAttr", value = "done"),
+})
+@RunWith(ApplicationComposer.class)
+public class ResourceLifecycleTest {
+    @Resource(name = "postConstructAndPreDestroy")
+    private WithBoth both;
+
+    @Resource(name = "postConstructOnly")
+    private WithPostConstruct postConstruct;
+
+    @Resource(name = "postConstructAnnotationAndConfigOnly")
+    private WithPostConstruct postConstruct2;
+
+    @Resource(name = "preDestroyOnly")
+    private WithPreDestroy preDestroy;
+
+    @Resource(name = "preDestroyAnnotationAndConfigOnly")
+    private WithPreDestroy preDestroy2;
+
+    private static final Collection<Runnable> POST_CONTAINER_VALIDATIONS = new LinkedList<>();
+
+    @AfterClass
+    public static void lastValidations() { // late to make the test failing (ie junit report will be broken) but better than destroying eagerly the resource
+        for (final Runnable runnable : POST_CONTAINER_VALIDATIONS) {
+            runnable.run();
+        }
+        POST_CONTAINER_VALIDATIONS.clear();
+    }
+
+    @Test
+    public void postConstructOnly() {
+        assertNotNull(postConstruct);
+        assertTrue(postConstruct.isInit());
+        assertFalse(postConstruct.isInit2());
+        assertEquals("done", postConstruct.myAttr);
+    }
+
+    @Test
+    public void postConstructAnnotationAndConfigOnly() {
+        assertNotNull(postConstruct2);
+        assertTrue(postConstruct2.isInit());
+        assertTrue(postConstruct2.isInit2());
+        assertEquals("done", postConstruct2.myAttr);
+    }
+
+    @Test
+    public void preDestroyOnly() {
+        assertNotNull(preDestroy);
+        assertFalse(preDestroy.isDestroy());
+        assertFalse(preDestroy.isDestroy2());
+        assertEquals("done", preDestroy.myAttr);
+        POST_CONTAINER_VALIDATIONS.add(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(preDestroy.isDestroy());
+            }
+        });
+    }
+
+    @Test
+    public void preDestroyAnnotationAndConfigOnly() {
+        assertNotNull(preDestroy2);
+        assertFalse(preDestroy2.isDestroy());
+        assertFalse(preDestroy2.isDestroy2());
+        assertEquals("done", preDestroy2.myAttr);
+        POST_CONTAINER_VALIDATIONS.add(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(preDestroy2.isDestroy());
+                assertTrue(preDestroy2.isDestroy2());
+            }
+        });
+    }
+
+    @Test
+    public void postConstructAndPreDestroy() {
+        assertNotNull(both);
+        assertTrue(both.isInit());
+        assertFalse(both.isDestroy());
+        assertEquals("done", both.myAttr);
+        POST_CONTAINER_VALIDATIONS.add(new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(both.isDestroy());
+            }
+        });
+    }
+
+    public static class TrivialConfigToCheckWarnings {
+        protected String myAttr;
+    }
+
+    public static class WithBoth extends TrivialConfigToCheckWarnings {
+        private boolean init;
+        private boolean destroy;
+
+        @PostConstruct
+        private void init() {
+            init = true;
+        }
+
+        @PreDestroy
+        private void init2() {
+            destroy = true;
+        }
+
+        public boolean isInit() {
+            return init;
+        }
+
+        public boolean isDestroy() {
+            return destroy;
+        }
+    }
+
+    public static class WithPostConstruct extends TrivialConfigToCheckWarnings {
+        private boolean init;
+        private boolean init2;
+
+        @PostConstruct
+        private void init() {
+            init = true;
+        }
+
+        private void init2() {
+            init2 = true;
+        }
+
+        public boolean isInit() {
+            return init;
+        }
+
+        public boolean isInit2() {
+            return init2;
+        }
+    }
+
+    public static class WithPreDestroy extends TrivialConfigToCheckWarnings {
+        private boolean destroy;
+        private boolean destroy2;
+
+        @PreDestroy
+        private void init() {
+            destroy = true;
+        }
+
+        private void init2() {
+            destroy2 = true;
+        }
+
+        public boolean isDestroy() {
+            return destroy;
+        }
+
+        public boolean isDestroy2() {
+            return destroy2;
+        }
+    }
+}