Skip to content

Commit

Permalink
Issue #12094 restore classloader association during comp/env creation
Browse files Browse the repository at this point in the history
  • Loading branch information
janbartel committed Jul 30, 2024
1 parent a1e90fe commit 558bd99
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,31 @@ public Object getObjectInstance(Object obj,
{
Context ctx = null;

//If the thread context classloader is set, then try its hierarchy to find a matching context
//See if there is a classloader already set to use for finding the comp
//naming Context
ClassLoader loader = (ClassLoader)__threadClassLoader.get();
if (loader != null)
{
if (LOG.isDebugEnabled())
LOG.debug("Using threadlocal classloader");
try (AutoLock l = __lock.lock())
{
ctx = getContextForClassLoader(loader);
if (ctx == null)
{
ctx = newNamingContext(obj, loader, env, name, nameCtx);
__contextMap.put(loader, ctx);
if (LOG.isDebugEnabled())
LOG.debug("Made context {} for classloader {}", name.get(0), loader);
}
return ctx;
}
}

//If the thread context classloader is set, then try it and its
//classloader hierarchy to find a matching naming Context
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
ClassLoader loader = tccl;
loader = tccl;
if (loader != null)
{
if (LOG.isDebugEnabled())
Expand All @@ -121,7 +143,7 @@ public Object getObjectInstance(Object obj,
}

//If trying thread context classloader hierarchy failed, try the
//classloader associated with the current context
//classloader associated with the current ContextHandler
if (ContextHandler.getCurrentContext() != null)
{
if (LOG.isDebugEnabled())
Expand Down Expand Up @@ -189,6 +211,18 @@ public Context getContextForClassLoader(ClassLoader loader)
}
}

public static ClassLoader associateClassLoader(final ClassLoader loader)
{
ClassLoader prev = (ClassLoader)__threadClassLoader.get();
__threadClassLoader.set(loader);
return prev;
}

public static void disassociateClassLoader()
{
__threadClassLoader.set(null);
}

public static void dump(Appendable out, String indent) throws IOException
{
try (AutoLock l = __lock.lock())
Expand Down
1 change: 1 addition & 0 deletions jetty-ee10/jetty-ee10-plus/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

// Only required if using Transaction.
requires static transitive jakarta.transaction;
requires org.eclipse.jetty.jndi;

exports org.eclipse.jetty.ee10.plus.jndi;
exports org.eclipse.jetty.ee10.plus.webapp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.ee10.webapp.WebXmlConfiguration;
import org.eclipse.jetty.jndi.ContextFactory;
import org.eclipse.jetty.plus.jndi.EnvEntry;
import org.eclipse.jetty.plus.jndi.NamingEntryUtil;
import org.eclipse.jetty.util.jndi.NamingDump;
Expand Down Expand Up @@ -123,6 +124,7 @@ public void deconfigure(WebAppContext context) throws Exception
//get rid of any bindings for comp/env for webapp
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
ContextFactory.associateClassLoader(context.getClassLoader());
try
{
Context ic = new InitialContext();
Expand All @@ -147,6 +149,7 @@ public void deconfigure(WebAppContext context) throws Exception
}
finally
{
ContextFactory.disassociateClassLoader();
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
Expand Down Expand Up @@ -218,6 +221,9 @@ protected void createEnvContext(WebAppContext wac)
{
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(wac.getClassLoader());
//ensure that we create a unique comp/env context for this webapp based off
//its classloader
ContextFactory.associateClassLoader(wac.getClassLoader());
try
{
Context context = new InitialContext();
Expand All @@ -226,6 +232,7 @@ protected void createEnvContext(WebAppContext wac)
}
finally
{
ContextFactory.disassociateClassLoader();
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,42 @@

import java.nio.file.Files;
import java.nio.file.Path;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.eclipse.jetty.ee.WebAppClassLoading;
import org.eclipse.jetty.ee10.webapp.Configuration;
import org.eclipse.jetty.ee10.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee10.webapp.WebAppContext;
import org.eclipse.jetty.plus.jndi.NamingEntryUtil;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Isolated;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Isolated("jndi entries")
public class EnvConfigurationTest
{
Server _server;

@BeforeAll
public static void exposeJNDIClasses()
{
//we need to expose the org.eclipse.jetty.jndi classes so the InitialContextFactory can be found
WebAppClassLoading.addHiddenClasses("-org.eclipse.jetty.jndi.");
}

@BeforeEach
public void setUp()
{
Expand Down Expand Up @@ -73,4 +92,75 @@ public void testWithJettyEEWebXml() throws Exception
assertNotNull(NamingEntryUtil.lookupNamingEntry(context, "peach"));
assertNull(NamingEntryUtil.lookupNamingEntry(context, "cabbage"));
}

@Test
public void testCompEnvCreation() throws Exception
{
EnvConfiguration envConfigurationA = new EnvConfiguration();
EnvConfiguration envConfigurationB = new EnvConfiguration();
WebAppContext webappA = null;
WebAppContext webappB = null;
try
{
webappA = new WebAppContext();
webappA.setConfigurations(new Configuration[]{new PlusConfiguration(), new EnvConfiguration()});
webappA.setClassLoader(new WebAppClassLoader(Thread.currentThread().getContextClassLoader(), webappA));

//ensure that a java:comp/env Context was created for webappA
envConfigurationA.preConfigure(webappA);
Context namingContextA = getCompEnvFor(webappA);

webappB = new WebAppContext();
webappB.setConfigurations(new Configuration[]{new PlusConfiguration(), new EnvConfiguration()});
webappB.setClassLoader(new WebAppClassLoader(Thread.currentThread().getContextClassLoader(), webappB));

//ensure that a different java:comp/env Context was created for webappB
envConfigurationB.preConfigure(webappB);
Context namingContextB = getCompEnvFor(webappB);

assertThat(namingContextA, is(not(namingContextB)));
}
finally
{
envConfigurationA.deconfigure(webappA);
envConfigurationB.deconfigure(webappB);
}
}

@Test
public void testPriorCompCreation() throws Exception
{
//pre-create java:comp on the app classloader
new InitialContext().lookup("java:comp");
//test that each webapp still gets its own naming Context
testCompEnvCreation();
}

/**
* Find the java:comp/env naming Context for the given webapp
* @param webapp the WebAppContext whose naming comp/env Context to find
* @return the comp/env naming Context specific to the given WebAppContext
* @throws NamingException
*/
private Context getCompEnvFor(WebAppContext webapp)
throws NamingException
{
if (webapp == null)
return null;

ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Context namingContext = null;
try
{
Thread.currentThread().setContextClassLoader(webapp.getClassLoader());
InitialContext ic = new InitialContext();
namingContext = (Context)ic.lookup("java:comp/env");
return namingContext;
}
finally
{
Thread.currentThread().setContextClassLoader(oldLoader);
}
}

}
1 change: 1 addition & 0 deletions jetty-ee9/jetty-ee9-plus/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

// Only required if using Transaction.
requires static transitive jakarta.transaction;
requires org.eclipse.jetty.jndi;

exports org.eclipse.jetty.ee9.plus.jndi;
exports org.eclipse.jetty.ee9.plus.webapp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.eclipse.jetty.ee9.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee9.webapp.WebAppContext;
import org.eclipse.jetty.ee9.webapp.WebXmlConfiguration;
import org.eclipse.jetty.jndi.ContextFactory;
import org.eclipse.jetty.plus.jndi.EnvEntry;
import org.eclipse.jetty.plus.jndi.NamingEntryUtil;
import org.eclipse.jetty.util.IO;
Expand Down Expand Up @@ -139,6 +140,7 @@ public void deconfigure(WebAppContext context) throws Exception
//get rid of any bindings for comp/env for webapp
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(context.getClassLoader());
ContextFactory.associateClassLoader(context.getClassLoader());
try
{
Context ic = new InitialContext();
Expand All @@ -163,6 +165,7 @@ public void deconfigure(WebAppContext context) throws Exception
}
finally
{
ContextFactory.disassociateClassLoader();
Thread.currentThread().setContextClassLoader(oldLoader);
IO.close(_resourceFactory);
_resourceFactory = null;
Expand Down Expand Up @@ -236,6 +239,10 @@ protected void createEnvContext(WebAppContext wac)
{
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(wac.getClassLoader());
//ensure that we create a unique comp/env context for this webapp based off
//its classloader
ContextFactory.associateClassLoader(wac.getClassLoader());

try
{
Context context = new InitialContext();
Expand All @@ -244,6 +251,7 @@ protected void createEnvContext(WebAppContext wac)
}
finally
{
ContextFactory.disassociateClassLoader();
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,29 @@

import java.nio.file.Files;
import java.nio.file.Path;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.eclipse.jetty.ee9.webapp.Configuration;
import org.eclipse.jetty.ee9.webapp.WebAppClassLoader;
import org.eclipse.jetty.ee9.webapp.WebAppContext;
import org.eclipse.jetty.plus.jndi.NamingEntryUtil;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.jndi.NamingUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Isolated;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Isolated("jndi entries")
public class EnvConfigurationTest
{
Server _server;
Expand Down Expand Up @@ -74,4 +83,76 @@ public void testWithJettyEEWebXml() throws Exception
assertNotNull(NamingEntryUtil.lookupNamingEntry(context, "peach"));
assertNull(NamingEntryUtil.lookupNamingEntry(context, "cabbage"));
}

@Test
public void testCompEnvCreation() throws Exception
{
EnvConfiguration envConfigurationA = new EnvConfiguration();
EnvConfiguration envConfigurationB = new EnvConfiguration();
WebAppContext webappA = null;
WebAppContext webappB = null;
try
{
webappA = new WebAppContext();
webappA.getServerClassMatcher().exclude("org.eclipse.jetty.jndi.");
webappA.setConfigurations(new Configuration[]{new PlusConfiguration(), new EnvConfiguration()});
webappA.setClassLoader(new WebAppClassLoader(Thread.currentThread().getContextClassLoader(), webappA));

//ensure that a java:comp/env Context was created for webappA
envConfigurationA.preConfigure(webappA);
Context namingContextA = getCompEnvFor(webappA);

webappB = new WebAppContext();
webappB.getServerClassMatcher().exclude("org.eclipse.jetty.jndi.");
webappB.setConfigurations(new Configuration[]{new PlusConfiguration(), new EnvConfiguration()});
webappB.setClassLoader(new WebAppClassLoader(Thread.currentThread().getContextClassLoader(), webappB));

//ensure that a different java:comp/env Context was created for webappB
envConfigurationB.preConfigure(webappB);
Context namingContextB = getCompEnvFor(webappB);

assertThat(namingContextA, is(not(namingContextB)));
}
finally
{
envConfigurationA.deconfigure(webappA);
envConfigurationB.deconfigure(webappB);
}
}

@Test
public void testPriorCompCreation() throws Exception
{
//pre-create java:comp on the app classloader
new InitialContext().lookup("java:comp");
//test that each webapp still gets its own naming Context
testCompEnvCreation();
}

/**
* Find the java:comp/env naming Context for the given webapp
* @param webapp the WebAppContext whose naming comp/env Context to find
* @return the comp/env naming Context specific to the given WebAppContext
* @throws NamingException
*/
private Context getCompEnvFor(WebAppContext webapp)
throws NamingException
{
if (webapp == null)
return null;

ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
Context namingContext = null;
try
{
Thread.currentThread().setContextClassLoader(webapp.getClassLoader());
InitialContext ic = new InitialContext();
namingContext = (Context)ic.lookup("java:comp/env");
return namingContext;
}
finally
{
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
}

0 comments on commit 558bd99

Please sign in to comment.