init
This commit is contained in:
46
test/org/apache/catalina/loader/EchoTag.java
Normal file
46
test/org/apache/catalina/loader/EchoTag.java
Normal 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();
|
||||
}
|
||||
}
|
||||
40
test/org/apache/catalina/loader/MyAnnotatedServlet.java
Normal file
40
test/org/apache/catalina/loader/MyAnnotatedServlet.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
335
test/org/apache/catalina/loader/TestVirtualContext.java
Normal file
335
test/org/apache/catalina/loader/TestVirtualContext.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
79
test/org/apache/catalina/loader/TestVirtualWebappLoader.java
Normal file
79
test/org/apache/catalina/loader/TestVirtualWebappLoader.java
Normal 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();
|
||||
}
|
||||
}
|
||||
175
test/org/apache/catalina/loader/TestWebappClassLoader.java
Normal file
175
test/org/apache/catalina/loader/TestWebappClassLoader.java
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(" }");
|
||||
}
|
||||
}
|
||||
24
test/org/apache/catalina/loader/TesterNeverWeavedClass.java
Normal file
24
test/org/apache/catalina/loader/TesterNeverWeavedClass.java
Normal 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.";
|
||||
}
|
||||
}
|
||||
24
test/org/apache/catalina/loader/TesterUnweavedClass.java
Normal file
24
test/org/apache/catalina/loader/TesterUnweavedClass.java
Normal 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!";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user