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/01/29 11:53:23 UTC

tomee git commit: TOMEE-1503 try to find dynamically if we should call parent first or not in a webapp of an ear

Repository: tomee
Updated Branches:
  refs/heads/develop 76e3e9e23 -> 9741e9518


TOMEE-1503 try to find dynamically if we should call parent first or not in a webapp of an ear


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

Branch: refs/heads/develop
Commit: 9741e9518c70554d9bee0e0a1f07a5756c7e75fc
Parents: 76e3e9e
Author: Romain Manni-Bucau <rm...@apache.org>
Authored: Thu Jan 29 11:52:53 2015 +0100
Committer: Romain Manni-Bucau <rm...@apache.org>
Committed: Thu Jan 29 11:52:53 2015 +0100

----------------------------------------------------------------------
 .../arquillian-tomee-jaxws-tests/pom.xml        |  6 ++
 .../tests/jaxws/EarClassLoaderTest.java         | 64 ++++++++++++++++
 .../jaxws/LoadJodaFromTheWebAppResource.java    | 37 +++++++++
 .../src/test/resources/arquillian.xml           |  1 +
 .../tomee/catalina/TomEEWebappClassLoader.java  | 79 +++++++++++++++++---
 5 files changed, 178 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tomee/blob/9741e951/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/pom.xml
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/pom.xml b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/pom.xml
index 7d9d0fc..ee883c7 100644
--- a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/pom.xml
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/pom.xml
@@ -36,6 +36,12 @@
       <version>${project.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency> <!-- org.apache.openejb.arquillian.tests.jaxws.EarClassLoaderTest -->
+      <groupId>joda-time</groupId>
+      <artifactId>joda-time</artifactId>
+      <version>2.5</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <properties>

http://git-wip-us.apache.org/repos/asf/tomee/blob/9741e951/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/EarClassLoaderTest.java
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/EarClassLoaderTest.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/EarClassLoaderTest.java
new file mode 100644
index 0000000..607d2c2
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/EarClassLoaderTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.jaxws;
+
+import org.apache.openejb.arquillian.common.IO;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.EnterpriseArchive;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
+import org.jboss.shrinkwrap.resolver.api.maven.strategy.AcceptScopesStrategy;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.net.URL;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(Arquillian.class)
+public class EarClassLoaderTest {
+    @Deployment(testable = false)
+    public static Archive<?> ear() {
+        return ShrinkWrap.create(EnterpriseArchive.class, "broken.ear")
+                .addAsModule(
+                        ShrinkWrap.create(WebArchive.class, "broken-web.war")
+                                .addClasses(LoadJodaFromTheWebAppResource.class)
+                                .addAsLibraries(
+                                        Maven.resolver()
+                                                .offline()
+                                                .resolve("joda-time:joda-time:2.5")
+                                                .withClassPathResolution(true)
+                                                .using(new AcceptScopesStrategy(ScopeType.COMPILE, ScopeType.RUNTIME))
+                                                .asFile()
+                                )
+                );
+    }
+
+    @ArquillianResource
+    private URL url;
+
+    @Test
+    public void checkIfWasCorretlyLoaded() throws IOException { // when writing this test we ship joda-time 2.2
+        assertEquals("2.5", IO.slurp(new URL(url.toExternalForm() + (url.getPath().isEmpty() ? "/broken-web/" : "") + "joda")));
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/9741e951/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/LoadJodaFromTheWebAppResource.java
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/LoadJodaFromTheWebAppResource.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/LoadJodaFromTheWebAppResource.java
new file mode 100644
index 0000000..bfac42c
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxws/LoadJodaFromTheWebAppResource.java
@@ -0,0 +1,37 @@
+/*
+ * 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.jaxws;
+
+import org.joda.time.LocalDateTime;
+
+import javax.ejb.Singleton;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+
+@Path("joda")
+@Singleton
+public class LoadJodaFromTheWebAppResource {
+    @GET
+    public String worked() {
+        LocalDateTime.now().toString(); // just trigger loading if not already done during scanning
+        return LocalDateTime.class.getPackage().getImplementationVersion();
+    }
+
+    public LocalDateTime triggerLoadingDuringScanning() {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tomee/blob/9741e951/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/resources/arquillian.xml
----------------------------------------------------------------------
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/resources/arquillian.xml b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/resources/arquillian.xml
index 710a9e5..d67d8f0 100644
--- a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/resources/arquillian.xml
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxws-tests/src/test/resources/arquillian.xml
@@ -43,6 +43,7 @@
         My\ DataSource.JdbcUrl = jdbc:hsqldb:mem:hsqldb
         My\ Unmanaged\ DataSource.JdbcUrl = jdbc:hsqldb:mem:hsqldb
         openejb.classloader.forced-load=org.apache.openejb.arquillian.tests
+        tomee.webapp-first=true
       </property>
     </configuration>
   </container>

http://git-wip-us.apache.org/repos/asf/tomee/blob/9741e951/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java
----------------------------------------------------------------------
diff --git a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java
index ef91219..0461d0e 100644
--- a/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java
+++ b/tomee/tomee-catalina/src/main/java/org/apache/tomee/catalina/TomEEWebappClassLoader.java
@@ -31,6 +31,7 @@ import org.apache.openejb.classloader.CompositeClassLoaderConfigurer;
 import org.apache.openejb.classloader.WebAppEnricher;
 import org.apache.openejb.config.NewLoaderLogic;
 import org.apache.openejb.config.QuickJarsTxtParser;
+import org.apache.openejb.core.ParentClassLoaderFinder;
 import org.apache.openejb.loader.Files;
 import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.util.LogCategory;
@@ -48,6 +49,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -75,22 +77,34 @@ public class TomEEWebappClassLoader extends ParallelWebappClassLoader {
         }
     }
 
+    public static final String CLASS_EXTENSION = ".class";
+
     private boolean restarting;
     private boolean forceStopPhase = Boolean.parseBoolean(SystemInstance.get().getProperty("tomee.webappclassloader.force-stop-phase", "true"));
     private ClassLoaderConfigurer configurer;
+    private final boolean isEar;
+    private final ClassLoader containerClassLoader;
+    private volatile boolean originalDelegate;
     private final int hashCode;
     private Collection<File> additionalRepos;
     private volatile boolean stopped = false;
+    private final Map<String, Boolean> filterTempCache = new HashMap<>(); // used only in sync block + isEar
 
     public TomEEWebappClassLoader() {
         hashCode = construct();
         setJavaseClassLoader(getSystemClassLoader());
+        containerClassLoader = ParentClassLoaderFinder.Helper.get();
+        isEar = getParent() != containerClassLoader;
+        originalDelegate = getDelegate();
     }
 
     public TomEEWebappClassLoader(final ClassLoader parent) {
         super(parent);
         hashCode = construct();
         setJavaseClassLoader(getSystemClassLoader());
+        containerClassLoader = ParentClassLoaderFinder.Helper.get();
+        isEar = getParent() != containerClassLoader;
+        originalDelegate = getDelegate();
     }
 
     private int construct() {
@@ -100,6 +114,12 @@ public class TomEEWebappClassLoader extends ParallelWebappClassLoader {
     }
 
     @Override
+    public void setDelegate(final boolean delegate) {
+        this.delegate = delegate;
+        this.originalDelegate = delegate;
+    }
+
+    @Override
     public void stop() throws LifecycleException {
         // in our destroyapplication method we need a valid classloader to TomcatWebAppBuilder.afterStop()
         if (forceStopPhase || restarting) {
@@ -123,13 +143,12 @@ public class TomEEWebappClassLoader extends ParallelWebappClassLoader {
             synchronized (this) {
                 final ClassLoader old = getJavaseClassLoader();
                 setJavaseClassLoader(NoClassClassLoader.INSTANCE);
-                final boolean delegate = getDelegate();
-                setDelegate(false);
+                delegate = false;
                 try {
                     return super.loadClass(name);
                 } finally {
                     setJavaseClassLoader(old);
-                    setDelegate(delegate);
+                    setDelegate(originalDelegate);
                 }
             }
         }
@@ -137,28 +156,59 @@ public class TomEEWebappClassLoader extends ParallelWebappClassLoader {
         // avoid to redefine classes from server in this classloader is it not already loaded
         if (URLClassLoaderFirst.shouldDelegateToTheContainer(this, name)) { // dynamic validation handling overriding
             try {
-                return OpenEJB.class.getClassLoader().loadClass(name);
+                return OpenEJB.class.getClassLoader().loadClass(name); // we could use containerClassLoader but this is server loader so cut it even more
             } catch (final ClassNotFoundException e) {
                 return super.loadClass(name);
             } catch (final NoClassDefFoundError ncdfe) {
                 return super.loadClass(name);
             }
         } else if (name.startsWith("javax.faces.") || name.startsWith("org.apache.webbeans.jsf.")) {
-            final boolean delegate = getDelegate();
             synchronized (this) {
-                setDelegate(false);
+                delegate = false;
                 try {
                     return super.loadClass(name);
                 } finally {
-                    setDelegate(delegate);
+                    setDelegate(originalDelegate);
                 }
             }
         }
-        synchronized (this) { // TODO: rework it to avoid it but not a big issue, see first if of this method
+        synchronized (this) { // TODO: rework it to avoid it and get aligned on Java 7 classloaders (but not a big issue)
+            if (isEar) {
+                final boolean filter = filter(name);
+                filterTempCache.put(name, filter); // will be called again by super.loadClass() so cache it
+                if (!filter && wouldBeLoadedFromContainer(name)) {
+                    setDelegate(false);
+                    try {
+                        return super.loadClass(name);
+                    } finally {
+                        filterTempCache.remove(name); // no more needed since class is loaded, avoid to waste mem
+                        setDelegate(originalDelegate);
+                    }
+                }
+            }
             return super.loadClass(name);
         }
     }
 
+    private boolean wouldBeLoadedFromContainer(final String name) {
+        final String resource = name.replace('.', '/') + CLASS_EXTENSION;
+
+        final URL containerUrl = containerClassLoader.getResource(resource);
+        if (containerUrl == null) {
+            return false;
+        }
+
+        final URL parentUrl = getParent().getResource(resource);
+        if (parentUrl == null) {
+            return false;
+        }
+        try {
+            return URLs.toFile(parentUrl).getCanonicalPath().equalsIgnoreCase(URLs.toFile(containerUrl).getCanonicalPath());
+        } catch (final IOException e) {
+            return false;
+        }
+    }
+
     @Override
     public void setResources(final WebResourceRoot resources) {
         this.resources = resources;
@@ -188,7 +238,18 @@ public class TomEEWebappClassLoader extends ParallelWebappClassLoader {
 
     @Override
     protected boolean filter(final String name) {
-        return !"org.apache.tomee.mojarra.TomEEInjectionProvider".equals(name) && URLClassLoaderFirst.shouldSkip(name);
+        if ("org.apache.tomee.mojarra.TomEEInjectionProvider".equals(name)) {
+            return false;
+        }
+        if (isEar) { // check we are called from super and we already cached the result in loadClass
+            synchronized (this) {
+                final Boolean cache = filterTempCache.get(name);
+                if (cache != null) {
+                    return cache;
+                }
+            }
+        }
+        return URLClassLoaderFirst.shouldSkip(name);
     }
 
     public void internalStop() throws LifecycleException {