This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
/*
* 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.catalina.loader;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
public class EchoTag extends TagSupport {
private static final long serialVersionUID = 1L;
private String echo = null;
public void setEcho(String echo) {
this.echo = echo;
}
public String getEcho() {
return echo;
}
@Override
public int doStartTag() throws JspException {
try {
pageContext.getOut().print("<p>" + echo + "</p>");
} catch (IOException e) {
throw new JspException(e);
}
return super.doStartTag();
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.catalina.loader;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(value = "/annotatedServlet")
public class MyAnnotatedServlet extends HttpServlet {
static final String MESSAGE = "This is generated by an annotated servlet";
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("test/plain");
resp.getWriter().println(MESSAGE);
}
}

View File

@@ -0,0 +1,335 @@
/*
* 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.catalina.loader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.core.JreMemoryLeakPreventionListener;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.apache.tomcat.util.scan.StandardJarScanner;
public class TestVirtualContext extends TomcatBaseTest {
@Override
public void setUp() throws Exception {
super.setUp();
Tomcat tomcat = getTomcatInstance();
// BZ 49218: The test fails if JreMemoryLeakPreventionListener is not
// present. The listener affects the JVM, and thus not only the current,
// but also the subsequent tests that are run in the same JVM. So it is
// fair to add it in every test.
tomcat.getServer().addLifecycleListener(
new JreMemoryLeakPreventionListener());
}
@Test
public void testVirtualClassLoader() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-virtual-webapp/src/main/webapp");
// app dir is relative to server home
StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test",
appDir.getAbsolutePath());
ctx.setResources(new StandardRoot(ctx));
File f1 = new File("test/webapp-virtual-webapp/target/classes");
File f2 = new File("test/webapp-virtual-library/target/WEB-INF");
File f3 = new File(
"test/webapp-virtual-webapp/src/main/webapp/WEB-INF/classes");
File f4 = new File(
"test/webapp-virtual-webapp/src/main/webapp2/WEB-INF/classes");
File f5 = new File("test/webapp-virtual-webapp/src/main/misc");
File f6 = new File("test/webapp-virtual-webapp/src/main/webapp2");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes",
f1.getAbsolutePath(), null, "/");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF",
f2.getAbsolutePath(), null, "/");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes",
f3.getAbsolutePath(), null, "/");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes",
f4.getAbsolutePath(), null, "/");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/other",
f5.getAbsolutePath(), null, "/");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/",
f6.getAbsolutePath(), null, "/");
StandardJarScanner jarScanner = new StandardJarScanner();
jarScanner.setScanAllDirectories(true);
ctx.setJarScanner(jarScanner);
ctx.setAddWebinfClassesResources(true);
tomcat.start();
assertPageContains("/test/classpathGetResourceAsStream.jsp?path=nonexistent",
"resourceAInWebInfClasses=true", 404);
assertPageContains(
"/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceA.properties",
"resourceAInWebInfClasses=true");
assertPageContains(
"/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceA.properties",
"resourceAInWebInfClasses=true");
assertPageContains(
"/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceB.properties",
"resourceBInTargetClasses=true");
assertPageContains(
"/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceB.properties",
"resourceBInTargetClasses=true");
assertPageContains(
"/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceC.properties",
"resourceCInDependentLibraryTargetClasses=true");
assertPageContains(
"/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceC.properties",
"resourceCInDependentLibraryTargetClasses=true");
assertPageContains(
"/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceD.properties",
"resourceDInPackagedJarInWebInfLib=true");
assertPageContains(
"/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceD.properties",
"resourceDInPackagedJarInWebInfLib=true");
assertPageContains(
"/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceG.properties",
"resourceGInWebInfClasses=true");
assertPageContains(
"/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceG.properties",
"resourceGInWebInfClasses=true");
// test listing all possible paths for a classpath resource
String allUrls =
getUrl(
"http://localhost:" + getPort() +
"/test/classpathGetResources.jsp?path=rsrc/").toString();
Assert.assertTrue(
allUrls,
allUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp/WEB-INF/classes/rsrc") > 0);
Assert.assertTrue(
allUrls,
allUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp2/WEB-INF/classes/rsrc") > 0);
Assert.assertTrue(
allUrls,
allUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp/WEB-INF/lib/rsrc.jar!/rsrc") > 0);
Assert.assertTrue(
allUrls,
allUrls.indexOf("/test/webapp-virtual-webapp/target/classes/rsrc") > 0);
Assert.assertTrue(
allUrls,
allUrls.indexOf("/test/webapp-virtual-library/target/WEB-INF/classes/rsrc") > 0);
// check that there's no duplicate in the URLs
String[] allUrlsArray = allUrls.split("\\s+");
Assert.assertEquals(new HashSet<>(Arrays.asList(allUrlsArray)).size(),
allUrlsArray.length);
String allRsrsc2ClasspathUrls =
getUrl(
"http://localhost:" + getPort() +
"/test/classpathGetResources.jsp?path=rsrc2/").toString();
Assert.assertTrue(
allRsrsc2ClasspathUrls,
allRsrsc2ClasspathUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp2/WEB-INF/classes/rsrc2") > 0);
// tests context.getRealPath
// the following fails because getRealPath always return a non-null path
// even if there's no such resource
// assertPageContains("/test/contextGetRealPath.jsp?path=nonexistent",
// "resourceAInWebInfClasses=true", 404);
// Real paths depend on the OS and this test has to work on all
// platforms so use File to convert the path to a platform specific form
File f = new File(
"test/webapp-virtual-webapp/src/main/webapp/rsrc/resourceF.properties");
assertPageContains(
"/test/contextGetRealPath.jsp?path=/rsrc/resourceF.properties",
f.getPath());
// tests context.getResource then the content
assertPageContains("/test/contextGetResource.jsp?path=/nonexistent",
"resourceAInWebInfClasses=true", 404);
assertPageContains(
"/test/contextGetResource.jsp?path=/WEB-INF/classes/rsrc/resourceA.properties",
"resourceAInWebInfClasses=true");
assertPageContains(
"/test/contextGetResource.jsp?path=/WEB-INF/classes/rsrc/resourceG.properties",
"resourceGInWebInfClasses=true");
assertPageContains(
"/test/contextGetResource.jsp?path=/rsrc/resourceE.properties",
"resourceEInDependentLibraryTargetClasses=true");
assertPageContains(
"/test/contextGetResource.jsp?path=/other/resourceI.properties",
"resourceIInWebapp=true");
assertPageContains(
"/test/contextGetResource.jsp?path=/rsrc2/resourceJ.properties",
"resourceJInWebapp=true");
String allRsrcPaths =
getUrl(
"http://localhost:" + getPort() +
"/test/contextGetResourcePaths.jsp?path=/rsrc/").toString();
Assert.assertTrue(
allRsrcPaths,
allRsrcPaths.indexOf("/rsrc/resourceF.properties") > 0);
Assert.assertTrue(
allRsrcPaths,
allRsrcPaths.indexOf("/rsrc/resourceE.properties") > 0);
Assert.assertTrue(
allRsrcPaths,
allRsrcPaths.indexOf("/rsrc/resourceH.properties") > 0);
// check that there's no duplicate in the URLs
String[] allRsrcPathsArray = allRsrcPaths.split("\\s+");
Assert.assertEquals(new HashSet<>(Arrays.asList(allRsrcPathsArray)).size(),
allRsrcPathsArray.length);
String allRsrc2Paths =
getUrl(
"http://localhost:" + getPort() +
"/test/contextGetResourcePaths.jsp?path=/rsrc2/").toString();
Assert.assertTrue(
allRsrc2Paths,
allRsrc2Paths.indexOf("/rsrc2/resourceJ.properties") > 0);
assertPageContains(
"/test/testTlds.jsp",
"worldA");
assertPageContains(
"/test/testTlds.jsp",
"worldB");
assertPageContains(
"/test/testTlds.jsp",
"worldC");
assertPageContains(
"/test/testTlds.jsp",
"worldD");
}
@Test
public void testAdditionalWebInfClassesPaths() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-virtual-webapp/src/main/webapp");
// app dir is relative to server home
StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test",
appDir.getAbsolutePath());
File tempFile = File.createTempFile("virtualWebInfClasses", null);
File additionWebInfClasses = new File(tempFile.getAbsolutePath() + ".dir");
Assert.assertTrue(additionWebInfClasses.mkdirs());
File targetPackageForAnnotatedClass =
new File(additionWebInfClasses,
MyAnnotatedServlet.class.getPackage().getName().replace('.', '/'));
Assert.assertTrue(targetPackageForAnnotatedClass.mkdirs());
try (InputStream annotatedServletClassInputStream = this.getClass().getResourceAsStream(
MyAnnotatedServlet.class.getSimpleName() + ".class");
FileOutputStream annotatedServletClassOutputStream = new FileOutputStream(new File(
targetPackageForAnnotatedClass, MyAnnotatedServlet.class.getSimpleName()
+ ".class"));) {
IOUtils.copy(annotatedServletClassInputStream, annotatedServletClassOutputStream);
}
ctx.setResources(new StandardRoot(ctx));
File f1 = new File("test/webapp-virtual-webapp/target/classes");
File f2 = new File("test/webapp-virtual-library/target/WEB-INF/classes");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes",
f1.getAbsolutePath(), null, "/");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes",
f2.getAbsolutePath(), null, "/");
tomcat.start();
// first test that without the setting on StandardContext the annotated
// servlet is not detected
assertPageContains("/test/annotatedServlet", MyAnnotatedServlet.MESSAGE, 404);
tomcat.stop();
// then test that if we configure StandardContext with the additional
// path, the servlet is detected
ctx.setResources(new StandardRoot(ctx));
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes",
f1.getAbsolutePath(), null, "/");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes",
f2.getAbsolutePath(), null, "/");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes",
additionWebInfClasses.getAbsolutePath(), null, "/");
tomcat.start();
assertPageContains("/test/annotatedServlet", MyAnnotatedServlet.MESSAGE);
tomcat.stop();
FileUtils.deleteDirectory(additionWebInfClasses);
Assert.assertTrue("Failed to clean up [" + tempFile + "]", tempFile.delete());
}
private void assertPageContains(String pageUrl, String expectedBody)
throws IOException {
assertPageContains(pageUrl, expectedBody, 200);
}
private void assertPageContains(String pageUrl, String expectedBody,
int expectedStatus) throws IOException {
ByteChunk res = new ByteChunk();
// Note: With a read timeout of 3s the ASF CI buildbot was consistently
// seeing failures with this test. The failures were due to the
// JSP initialisation taking longer than the read timeout. The
// root cause of this is the frequent poor IO performance of the
// VM running the buildbot instance. Increasing this to 10s should
// avoid these failures.
int sc = getUrl("http://localhost:" + getPort() + pageUrl, res, 10000,
null, null);
Assert.assertEquals(expectedStatus, sc);
if (expectedStatus == 200) {
String result = res.toString();
Assert.assertTrue(result, result.indexOf(expectedBody) >= 0);
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.catalina.loader;
import java.io.File;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.webresources.StandardRoot;
public class TestVirtualWebappLoader extends TomcatBaseTest {
@Test
public void testModified() throws Exception {
WebappLoader loader = new WebappLoader();
Assert.assertNull(loader.getClassLoader());
Assert.assertFalse(loader.modified());
}
@Test
public void testStartInternal() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp");
StandardContext ctx = (StandardContext) tomcat.addContext("",
appDir.getAbsolutePath());
WebappLoader loader = new WebappLoader();
loader.setContext(ctx);
ctx.setLoader(loader);
ctx.setResources(new StandardRoot(ctx));
ctx.resourcesStart();
File f1 = new File("test/webapp-fragments/WEB-INF/lib");
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/lib",
f1.getAbsolutePath(), null, "/");
loader.start();
String[] repos = loader.getLoaderRepositories();
Assert.assertEquals(4,repos.length);
loader.stop();
repos = loader.getLoaderRepositories();
Assert.assertEquals(0, repos.length);
// no leak
loader.start();
repos = loader.getLoaderRepositories();
Assert.assertEquals(4,repos.length);
// clear loader
ctx.setLoader(null);
// see tearDown()!
tomcat.start();
}
}

View File

@@ -0,0 +1,175 @@
/*
* 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.catalina.loader;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
public class TestWebappClassLoader extends TomcatBaseTest {
@Test
public void testGetURLs() throws Exception {
File f = new File("test/webresources/war-url-connection.war");
String[] expected = new String[2];
String warUrl = f.toURI().toURL().toExternalForm();
expected[0] = "war:" + warUrl + "*/WEB-INF/classes/";
expected[1] = "war:" + warUrl + "*/WEB-INF/lib/test.jar";
Tomcat tomcat = getTomcatInstance();
StandardContext ctx =
(StandardContext)tomcat.addContext("", f.getAbsolutePath());
tomcat.start();
ClassLoader cl = ctx.getLoader().getClassLoader();
Assert.assertTrue(cl instanceof URLClassLoader);
try (URLClassLoader ucl = (URLClassLoader) cl) {
URL[] urls = ucl.getURLs();
Assert.assertEquals(expected.length, urls.length);
String[] actual = new String[urls.length];
for (int i = 0; i < urls.length; i++) {
actual[i] = urls[i].toExternalForm();
}
Assert.assertArrayEquals(expected, actual);
}
}
@Test
public void testFilter() throws IOException {
String[] classSuffixes = new String[]{
"",
"some.package.Example"
};
String[] resourceSuffixes = new String[]{
"",
"some/path/test.properties",
"some/path/test"
};
String[] prefixes = new String[]{
"",
"resources",
"WEB-INF",
"WEB-INF.classes",
"WEB-INF.lib",
"org",
"org.apache",
"javax",
"com.mycorp"
};
String[] prefixesPermit = new String[]{
"org.apache.tomcat.jdbc",
"javax.servlet.jsp.jstl",
};
String[] prefixesDeny = new String[]{
"org.apache.catalina",
"org.apache.coyote",
"org.apache.el",
"org.apache.jasper",
"org.apache.juli",
"org.apache.naming",
"org.apache.tomcat",
"javax.el",
"javax.servlet",
"javax.websocket",
"javax.security.auth.message"
};
try (WebappClassLoader loader = new WebappClassLoader()) {
String name;
for (String prefix : prefixes) {
for (String suffix : classSuffixes) {
name = prefix + "." + suffix;
Assert.assertTrue("Class '" + name + "' failed permit filter",
!loader.filter(name, true));
if (prefix.equals("")) {
name = suffix;
Assert.assertTrue("Class '" + name + "' failed permit filter",
!loader.filter(name, true));
}
if (suffix.equals("")) {
name = prefix;
Assert.assertTrue("Class '" + name + "' failed permit filter",
!loader.filter(name, true));
}
}
prefix = prefix.replace('.', '/');
for (String suffix : resourceSuffixes) {
name = prefix + "/" + suffix;
Assert.assertTrue("Resource '" + name + "' failed permit filter",
!loader.filter(name, false));
if (prefix.equals("")) {
name = suffix;
Assert.assertTrue("Resource '" + name + "' failed permit filter",
!loader.filter(name, false));
}
if (suffix.equals("")) {
name = prefix;
Assert.assertTrue("Resource '" + name + "' failed permit filter",
!loader.filter(name, false));
}
}
}
for (String prefix : prefixesPermit) {
for (String suffix : classSuffixes) {
name = prefix + "." + suffix;
Assert.assertTrue("Class '" + name + "' failed permit filter",
!loader.filter(name, true));
}
prefix = prefix.replace('.', '/');
for (String suffix : resourceSuffixes) {
name = prefix + "/" + suffix;
Assert.assertTrue("Resource '" + name + "' failed permit filter",
!loader.filter(name, false));
}
}
for (String prefix : prefixesDeny) {
for (String suffix : classSuffixes) {
name = prefix + "." + suffix;
Assert.assertTrue("Class '" + name + "' failed deny filter",
loader.filter(name, true));
}
prefix = prefix.replace('.', '/');
for (String suffix : resourceSuffixes) {
name = prefix + "/" + suffix;
Assert.assertTrue("Resource '" + name + "' failed deny filter",
loader.filter(name, false));
}
}
}
}
}

View File

@@ -0,0 +1,131 @@
/*
* 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.catalina.loader;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
public class TestWebappClassLoaderExecutorMemoryLeak extends TomcatBaseTest {
@Test
public void testTimerThreadLeak() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
if (ctx instanceof StandardContext) {
((StandardContext) ctx).setClearReferencesStopThreads(true);
}
ExecutorServlet executorServlet = new ExecutorServlet();
Tomcat.addServlet(ctx, "taskServlet", executorServlet);
ctx.addServletMappingDecoded("/", "taskServlet");
tomcat.start();
// This will trigger the timer & thread creation
getUrl("http://localhost:" + getPort() + "/");
// Stop the context
ctx.stop();
// Should be shutdown once the stop() method above exists
Assert.assertTrue(executorServlet.tpe.isShutdown());
// The time taken to shutdown the executor can vary between systems. Try
// to avoid false test failures due to timing issues. Give the executor
// upto 10 seconds to close down.
int count = 0;
while (count < 100 && !executorServlet.tpe.isTerminated()) {
count++;
Thread.sleep(100);
}
// If the executor has not terminated, there is a thread/memory leak
Assert.assertTrue(executorServlet.tpe.isTerminated());
}
static class ExecutorServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
int nTasks = 5;
long n = 1000L;
int tpSize = 10;
public transient volatile ThreadPoolExecutor tpe;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().println(
"The current thread served " + this + " servlet");
tpe = new ThreadPoolExecutor(tpSize, tpSize, 50000L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
Task[] tasks = new Task[nTasks];
for (int i = 0; i < nTasks; i++) {
tasks[i] = new Task("Task " + i);
tpe.execute(tasks[i]);
}
resp.getWriter().println("Started " + nTasks +
" never ending tasks using the ThreadPoolExecutor");
resp.getWriter().flush();
}
class Task implements Runnable {
String _id;
public Task(String id) {
this._id = id;
}
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
Thread.sleep(20000);
System.out.println(Thread.currentThread().getClass()
+ " [" + Thread.currentThread().getName()
+ "] executing " + this._id);
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getClass() + " ["
+ Thread.currentThread().getName() + "] EXITING");
}
}
}
}
}

View File

@@ -0,0 +1,121 @@
/*
* 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.catalina.loader;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
public class TestWebappClassLoaderMemoryLeak extends TomcatBaseTest {
@Test
public void testTimerThreadLeak() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
if (ctx instanceof StandardContext) {
((StandardContext) ctx).setClearReferencesStopTimerThreads(true);
}
Tomcat.addServlet(ctx, "taskServlet", new TaskServlet());
ctx.addServletMappingDecoded("/", "taskServlet");
tomcat.start();
// This will trigger the timer & thread creation
getUrl("http://localhost:" + getPort() + "/");
// Stop the context
ctx.stop();
Thread[] threads = getThreads();
for (Thread thread : threads) {
if (thread != null && thread.isAlive() &&
TaskServlet.TIMER_THREAD_NAME.equals(thread.getName())) {
thread.join(5000);
if (thread.isAlive()) {
Assert.fail("Timer thread still running");
}
}
}
}
/*
* Get the set of current threads as an array.
* Copied from WebappClassLoaderBase
*/
private Thread[] getThreads() {
// Get the current thread group
ThreadGroup tg = Thread.currentThread( ).getThreadGroup( );
// Find the root thread group
while (tg.getParent() != null) {
tg = tg.getParent();
}
int threadCountGuess = tg.activeCount() + 50;
Thread[] threads = new Thread[threadCountGuess];
int threadCountActual = tg.enumerate(threads);
// Make sure we don't miss any threads
while (threadCountActual == threadCountGuess) {
threadCountGuess *=2;
threads = new Thread[threadCountGuess];
// Note tg.enumerate(Thread[]) silently ignores any threads that
// can't fit into the array
threadCountActual = tg.enumerate(threads);
}
return threads;
}
private static final class TaskServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String TIMER_THREAD_NAME = "leaked-thread";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Timer timer = new Timer(TIMER_THREAD_NAME);
timer.schedule(new LocalTask(), 0, 10000);
}
}
private static final class LocalTask extends TimerTask {
@Override
public void run() {
// Doesn't actually need to do anything.
}
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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.catalina.loader;
import java.io.InputStream;
import java.util.concurrent.Executor;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.core.JreMemoryLeakPreventionListener;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.unittest.TesterLogValidationFilter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
/*
* These unit tests are ignored by default as they are not reliable. They have
* been failing regularly on Gump for some time and have recently started to
* fail regularly on markt's laptop.
*
* The problem is that the ThreadLocal Maps are affected by GC. If GC occurs at
* the wrong point, the leaking ThreadLocal will be cleaned up and the test will
* fail. It is not possible to force the test to pass without effectively
* changing the nature of the test so it no longer tests detection of leaks via
* ThreadLocals.
*
* The test has been left in place since it will work reasonably reliably on
* most systems (just not all and particularly some of the ASF's CI systems) and
* still may be useful if a bug is reported in this area in the future.
*/
@Ignore
public class TestWebappClassLoaderThreadLocalMemoryLeak extends TomcatBaseTest {
@Test
public void testThreadLocalLeak1() throws Exception {
Tomcat tomcat = getTomcatInstance();
// Need to make sure we see a leak for the right reasons
tomcat.getServer().addLifecycleListener(
new JreMemoryLeakPreventionListener());
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "leakServlet1",
"org.apache.tomcat.unittest.TesterLeakingServlet1");
ctx.addServletMappingDecoded("/leak1", "leakServlet1");
tomcat.start();
Executor executor = tomcat.getConnector().getProtocolHandler().getExecutor();
((ThreadPoolExecutor) executor).setThreadRenewalDelay(-1);
// Configure logging filter to check leak message appears
TesterLogValidationFilter f = TesterLogValidationFilter.add(null,
"The web application [ROOT] created a ThreadLocal with key of", null,
"org.apache.catalina.loader.WebappClassLoaderBase");
// Need to force loading of all web application classes via the web
// application class loader
loadClass("TesterCounter",
(WebappClassLoaderBase) ctx.getLoader().getClassLoader());
loadClass("TesterLeakingServlet1",
(WebappClassLoaderBase) ctx.getLoader().getClassLoader());
// This will trigger the ThreadLocal creation
int rc = getUrl("http://localhost:" + getPort() + "/leak1",
new ByteChunk(), null);
// Make sure request is OK
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
// Destroy the context
ctx.stop();
tomcat.getHost().removeChild(ctx);
ctx = null;
// Make sure we have a memory leak
String[] leaks = ((StandardHost) tomcat.getHost())
.findReloadedContextMemoryLeaks();
Assert.assertNotNull(leaks);
Assert.assertTrue(leaks.length > 0);
// Make sure the message was logged
Assert.assertEquals(1, f.getMessageCount());
}
@Test
public void testThreadLocalLeak2() throws Exception {
Tomcat tomcat = getTomcatInstance();
// Need to make sure we see a leak for the right reasons
tomcat.getServer().addLifecycleListener(
new JreMemoryLeakPreventionListener());
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "leakServlet2",
"org.apache.tomcat.unittest.TesterLeakingServlet2");
ctx.addServletMappingDecoded("/leak2", "leakServlet2");
tomcat.start();
Executor executor = tomcat.getConnector().getProtocolHandler().getExecutor();
((ThreadPoolExecutor) executor).setThreadRenewalDelay(-1);
// Configure logging filter to check leak message appears
TesterLogValidationFilter f = TesterLogValidationFilter.add(null,
"The web application [ROOT] created a ThreadLocal with key of", null,
"org.apache.catalina.loader.WebappClassLoaderBase");
// Need to force loading of all web application classes via the web
// application class loader
loadClass("TesterCounter",
(WebappClassLoaderBase) ctx.getLoader().getClassLoader());
loadClass("TesterThreadScopedHolder",
(WebappClassLoaderBase) ctx.getLoader().getClassLoader());
loadClass("TesterLeakingServlet2",
(WebappClassLoaderBase) ctx.getLoader().getClassLoader());
// This will trigger the ThreadLocal creation
int rc = getUrl("http://localhost:" + getPort() + "/leak2",
new ByteChunk(), null);
// Make sure request is OK
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
// Destroy the context
ctx.stop();
tomcat.getHost().removeChild(ctx);
ctx = null;
// Make sure we have a memory leak
String[] leaks = ((StandardHost) tomcat.getHost())
.findReloadedContextMemoryLeaks();
Assert.assertNotNull(leaks);
Assert.assertTrue(leaks.length > 0);
// Make sure the message was logged
Assert.assertEquals(1, f.getMessageCount());
}
/**
* Utility method to ensure that classes are loaded by the
* WebappClassLoader. We can't just create classes since they will be loaded
* by the current class loader rather than the WebappClassLoader. This would
* mean that no leak occurred making the test for a leak rather pointless
* So, we load the bytes via the current class loader but define the class
* with the WebappClassLoader.
*
* This method assumes that all classes are in the current package.
*/
private void loadClass(String name, WebappClassLoaderBase cl) throws Exception {
try (InputStream is = cl.getResourceAsStream(
"org/apache/tomcat/unittest/" + name + ".class")) {
// We know roughly how big the class will be (~ 1K) so allow 2k as a
// starting point
byte[] classBytes = new byte[2048];
int offset = 0;
int read = is.read(classBytes, offset, classBytes.length-offset);
while (read > -1) {
offset += read;
if (offset == classBytes.length) {
// Buffer full - double size
byte[] tmp = new byte[classBytes.length * 2];
System.arraycopy(classBytes, 0, tmp, 0, classBytes.length);
classBytes = tmp;
}
read = is.read(classBytes, offset, classBytes.length-offset);
}
Class<?> lpClass = cl.doDefineClass(
"org.apache.tomcat.unittest." + name, classBytes, 0,
offset, cl.getClass().getProtectionDomain());
// Make sure we can create an instance
lpClass.getConstructor().newInstance();
}
}
}

View File

@@ -0,0 +1,412 @@
/*
* 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.catalina.loader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.http.fileupload.FileUtils;
public class TestWebappClassLoaderWeaving extends TomcatBaseTest {
private static final String PACKAGE_PREFIX = "org/apache/catalina/loader";
private static String WEBAPP_DOC_BASE;
@BeforeClass
public static void setUpClass() throws Exception {
String webappDocBase = "test/tmpTestWebappClassLoaderWeaving";
File webappDocBaseFile = new File(webappDocBase);
WEBAPP_DOC_BASE = webappDocBaseFile.getCanonicalPath();
File classes = new File(webappDocBaseFile, "/WEB-INF/classes/" + PACKAGE_PREFIX);
Assert.assertTrue("Failed to create [" + classes + "]", classes.mkdirs());
copyResource(PACKAGE_PREFIX + "/TesterNeverWeavedClass.class",
new File(classes, "TesterNeverWeavedClass.class"));
copyResource(PACKAGE_PREFIX + "/TesterUnweavedClass.class",
new File(classes, "TesterUnweavedClass.class"));
}
@AfterClass
public static void tearDownClass() throws Exception {
FileUtils.deleteDirectory(new File(WEBAPP_DOC_BASE));
}
private Tomcat tomcat;
private Context context;
private WebappClassLoaderBase loader;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
this.tomcat = getTomcatInstance();
this.context = this.tomcat.addContext("/weaving", WEBAPP_DOC_BASE);
this.tomcat.start();
ClassLoader loader = this.context.getLoader().getClassLoader();
Assert.assertNotNull("The class loader should not be null.", loader);
Assert.assertTrue("The class loader is not correct.", loader instanceof WebappClassLoaderBase);
this.loader = (WebappClassLoaderBase) loader;
}
@After
@Override
public void tearDown() throws Exception {
try {
this.loader = null;
this.context.stop();
this.tomcat.getHost().removeChild(this.context);
this.context = null;
} finally {
super.tearDown();
}
}
@Test
public void testNoWeaving() throws Exception {
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Unweaved World!", result);
}
@Test
public void testAddingNullTransformerThrowsException() throws Exception {
try {
this.loader.addTransformer(null);
Assert.fail("Expected exception IllegalArgumentException, got no exception.");
} catch (IllegalArgumentException ignore) {
// good
}
// class loading should still work, no weaving
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Unweaved World!", result);
}
@Test
public void testAddedTransformerInstrumentsClass1() throws Exception {
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_1));
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Weaver #1!", result);
}
@Test
public void testAddedTransformerInstrumentsClass2() throws Exception {
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_2));
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Weaver #2!", result);
}
@Test
public void testTransformersExecuteInOrderAdded1() throws Exception {
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_1));
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_2));
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Weaver #2!", result);
}
@Test
public void testTransformersExecuteInOrderAdded2() throws Exception {
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_2));
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_1));
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Weaver #1!", result);
}
@Test
public void testRemovedTransformerNoLongerInstruments1() throws Exception {
ReplacementTransformer removed = new ReplacementTransformer(WEAVED_REPLACEMENT_1);
this.loader.addTransformer(removed);
this.loader.removeTransformer(removed);
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Unweaved World!", result);
}
@Test
public void testRemovedTransformerNoLongerInstruments2() throws Exception {
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_1));
ReplacementTransformer removed = new ReplacementTransformer(WEAVED_REPLACEMENT_2);
this.loader.addTransformer(removed);
this.loader.removeTransformer(removed);
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Weaver #1!", result);
}
@Test
public void testRemovedTransformerNoLongerInstruments3() throws Exception {
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_2));
ReplacementTransformer removed = new ReplacementTransformer(WEAVED_REPLACEMENT_1);
this.loader.addTransformer(removed);
this.loader.removeTransformer(removed);
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Weaver #2!", result);
}
@Test
public void testCopiedClassLoaderExcludesResourcesAndTransformers() throws Exception {
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_1));
this.loader.addTransformer(new ReplacementTransformer(WEAVED_REPLACEMENT_2));
String result = invokeDoMethodOnClass(this.loader, "TesterNeverWeavedClass");
Assert.assertEquals("The first result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(this.loader, "TesterUnweavedClass");
Assert.assertEquals("The second result is not correct.", "Hello, Weaver #2!", result);
WebappClassLoaderBase copiedLoader = (WebappClassLoaderBase) this.loader.copyWithoutTransformers();
result = invokeDoMethodOnClass(copiedLoader, "TesterNeverWeavedClass");
Assert.assertEquals("The third result is not correct.", "This will never be weaved.", result);
result = invokeDoMethodOnClass(copiedLoader, "TesterUnweavedClass");
Assert.assertEquals("The fourth result is not correct.", "Hello, Unweaved World!", result);
Assert.assertEquals("getClearReferencesHttpClientKeepAliveThread did not match.",
Boolean.valueOf(this.loader.getClearReferencesHttpClientKeepAliveThread()),
Boolean.valueOf(copiedLoader.getClearReferencesHttpClientKeepAliveThread()));
Assert.assertEquals("getClearReferencesLogFactoryRelease did not match.",
Boolean.valueOf(this.loader.getClearReferencesLogFactoryRelease()),
Boolean.valueOf(copiedLoader.getClearReferencesLogFactoryRelease()));
Assert.assertEquals("getClearReferencesStopThreads did not match.",
Boolean.valueOf(this.loader.getClearReferencesStopThreads()),
Boolean.valueOf(copiedLoader.getClearReferencesStopThreads()));
Assert.assertEquals("getClearReferencesStopTimerThreads did not match.",
Boolean.valueOf(this.loader.getClearReferencesStopTimerThreads()),
Boolean.valueOf(copiedLoader.getClearReferencesStopTimerThreads()));
Assert.assertEquals("getContextName did not match.", this.loader.getContextName(),
copiedLoader.getContextName());
Assert.assertEquals("getDelegate did not match.",
Boolean.valueOf(this.loader.getDelegate()),
Boolean.valueOf(copiedLoader.getDelegate()));
Assert.assertEquals("getURLs did not match.", this.loader.getURLs().length,
copiedLoader.getURLs().length);
Assert.assertSame("getParent did not match.", this.loader.getParent(), copiedLoader.getParent());
}
private static void copyResource(String name, File file) throws Exception {
ClassLoader cl = TestWebappClassLoaderWeaving.class.getClassLoader();
try (InputStream is = cl.getResourceAsStream(name)) {
if (is == null) {
throw new IOException("Resource " + name + " not found on classpath.");
}
try (FileOutputStream os = new FileOutputStream(file)) {
for (int b = is.read(); b >= 0; b = is.read()) {
os.write(b);
}
}
}
}
private static String invokeDoMethodOnClass(WebappClassLoaderBase loader, String className)
throws Exception {
Class<?> c = loader.findClass("org.apache.catalina.loader." + className);
Assert.assertNotNull("The loaded class should not be null.", c);
Method m = c.getMethod("doMethod");
Object o = c.getConstructor().newInstance();
return (String) m.invoke(o);
}
private static class ReplacementTransformer implements ClassFileTransformer {
private static final String CLASS_TO_WEAVE = PACKAGE_PREFIX + "/TesterUnweavedClass";
private final byte[] replacement;
ReplacementTransformer(byte[] replacement) {
this.replacement = replacement;
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> x,
ProtectionDomain y, byte[] b) {
if (CLASS_TO_WEAVE.equals(className)) {
return this.replacement;
}
return null;
}
}
/**
* Compiled version of org.apache.catalina.loader.TesterUnweavedClass, except that
* the doMethod method returns "Hello, Weaver #1!". Compiled with Oracle Java 1.6.0_51.
*/
private static final byte[] WEAVED_REPLACEMENT_1 = new byte[] {
-54, -2, -70, -66, 0, 0, 0, 50, 0, 17, 10, 0, 4, 0, 13, 8, 0, 14, 7, 0, 15, 7, 0, 16, 1,
0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0,
15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 8, 100,
111, 77, 101, 116, 104, 111, 100, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97,
110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70,
105, 108, 101, 1, 0, 24, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, 101,
100, 67, 108, 97, 115, 115, 46, 106, 97, 118, 97, 12, 0, 5, 0, 6, 1, 0, 17, 72, 101,
108, 108, 111, 44, 32, 87, 101, 97, 118, 101, 114, 32, 35, 49, 33, 1, 0, 46, 111, 114,
103, 47, 97, 112, 97, 99, 104, 101, 47, 99, 97, 116, 97, 108, 105, 110, 97, 47, 108,
111, 97, 100, 101, 114, 47, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118,
101, 100, 67, 108, 97, 115, 115, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47,
79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1,
0, 7, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 8, 0, 0,
0, 6, 0, 1, 0, 0, 0, 19, 0, 1, 0, 9, 0, 10, 0, 1, 0, 7, 0, 0, 0, 27, 0, 1, 0, 1, 0, 0,
0, 3, 18, 2, -80, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 22, 0, 1, 0, 11, 0, 0, 0,
2, 0, 12
};
/**
* Compiled version of org.apache.catalina.loader.TesterUnweavedClass, except that
* the doMethod method returns "Hello, Weaver #2!". Compiled with Oracle Java 1.6.0_51.
*/
private static final byte[] WEAVED_REPLACEMENT_2 = new byte[] {
-54, -2, -70, -66, 0, 0, 0, 50, 0, 17, 10, 0, 4, 0, 13, 8, 0, 14, 7, 0, 15, 7, 0, 16, 1,
0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0,
15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 8, 100,
111, 77, 101, 116, 104, 111, 100, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97,
110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70,
105, 108, 101, 1, 0, 24, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, 101,
100, 67, 108, 97, 115, 115, 46, 106, 97, 118, 97, 12, 0, 5, 0, 6, 1, 0, 17, 72, 101,
108, 108, 111, 44, 32, 87, 101, 97, 118, 101, 114, 32, 35, 50, 33, 1, 0, 46, 111, 114,
103, 47, 97, 112, 97, 99, 104, 101, 47, 99, 97, 116, 97, 108, 105, 110, 97, 47, 108,
111, 97, 100, 101, 114, 47, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118,
101, 100, 67, 108, 97, 115, 115, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47,
79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1,
0, 7, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 8, 0, 0,
0, 6, 0, 1, 0, 0, 0, 19, 0, 1, 0, 9, 0, 10, 0, 1, 0, 7, 0, 0, 0, 27, 0, 1, 0, 1, 0, 0,
0, 3, 18, 2, -80, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 22, 0, 1, 0, 11, 0, 0, 0,
2, 0, 12
};
/*
* The WEAVED_REPLACEMENT_1 and WEAVED_REPLACEMENT_2 field contents are generated using the
* following code. To regenerate them, alter the TesterUnweavedClass code as desired, recompile,
* and run this main method.
*/
public static void main(String... arguments) throws Exception {
ClassLoader cl = TestWebappClassLoaderWeaving.class.getClassLoader();
try (InputStream input = cl.getResourceAsStream(
"org/apache/catalina/loader/TesterUnweavedClass.class")) {
StringBuilder builder = new StringBuilder();
builder.append(" ");
System.out.println(" private static final byte[] WEAVED_REPLACEMENT_1 = new byte[] {");
for (int i = 0, b = input.read(); b >= 0; i++, b = input.read()) {
String value = "" + ((byte)b);
if (builder.length() + value.length() > 97) {
builder.append(",");
System.out.println(builder.toString());
builder = new StringBuilder();
builder.append(" ").append(value);
} else {
if (i > 0) {
builder.append(", ");
}
builder.append(value);
}
}
System.out.println(builder.toString());
}
System.out.println(" }");
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.catalina.loader;
public class TesterNeverWeavedClass {
public String doMethod() {
return "This will never be weaved.";
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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.catalina.loader;
public class TesterUnweavedClass {
public String doMethod() {
return "Hello, Unweaved World!";
}
}