我正在尝试使用Web上下文启动Jetty服务器。我传递的web.xml
包含一个自定义类(由于不同的原因)在执行下面代码的代码的类路径上。我希望Jetty无法找到web.xml
中指定的类,但确实如此。
我如何构建一个new Server
WebAppContext
从当前上下文加载类?
以下代码在Scala中,但它应该让您了解我尝试过的内容。如果你用Java回答完全没问题。
// A reference to the root class loader, this one should not know
// about any of my custom classes
val rootLoader = {
// Recursive method to find the loader without parent
def parent(loader:ClassLoader):ClassLoader = {
val p = loader.getParent
if (p == null) loader else parent(p)
}
parent(getClass.getClassLoader)
}
// Check if the custom ClassFile is available from the rootLoader
val className = classOf[my.custom.servlet.ClassFile].getName
val classAvailable =
try {
rootLoader.loadClass(className)
true
} catch {
case e:ClassNotFoundException => false
}
// If I'm not insane no error is thrown
if (classAvailable) sys.error("Class should not be available")
// Create a WebAppContext using an empty WebAppClassLoader
// based on that rootLoader
val context = new WebAppContext()
context.setParentLoaderPriority(false)
//context.setClassLoader(new WebAppClassLoader(rootLoader, context))
context.setClassLoader(rootLoader)
context.setContextPath("/")
context.setDescriptor(webXmlFile)
context.setResourceBase(resourceBase)
// Create an start the server with the context
val server = new Server(port)
server.setHandler(context)
server.start()
我预计上下文不可以找到相关的课程。
答案 0 :(得分:2)
好的EEColor,我已经在代码中进行了挖掘,我认为我们已经解决了两个问题:
现在,错误#1就是为什么即使你设置一个完全独立的类加载器,webapp仍然可以神奇地加载类。 Bug#2不应该真正影响你,除非你开始讨论Joakim关于设置系统和服务器类的建议,即便如此你也可以,因为该类存在于容器类加载器父层次结构和webapp类加载器中只有在容器类加载器中缺少系统类但存在于webapp类加载器中时才会出现错误。
因此,您可以尝试使用Joakim建议将类添加为系统类,这应该可行。或者,您还应该能够简单地设置WebAppContext.setParentLoaderPriority(true),并且还将首先尝试容器类加载器层次结构。虽然请注意setParentPriority(true)将应用于webapp的所有类加载,而Joakim的建议将不同的加载行为限制为仅涉及的类。
扬
答案 1 :(得分:1)
问题没有明确说明,很难理解目标是什么。
提示:将来,询问如何达成目标,然后说明您尝试的路径
假设您有一个名为example-webapp.war
它有一个简单的WEB-INF/web.xml
,其中包含一个servlet条目......
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>com.company.foo.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
example-webapp.war
带有自己的com.company.foo.TestServlet
(可在webapp的WEB-INF/classes
目录中找到)
现在,您有一个embedded-jetty实例,您想要启动example-webapp.war
,但是您希望使用com.company.foo.TestServlet
之外的example-webapp.war
实现(具有不同的功能) )。
您可以通过首先认识到servlet规范从其服务器具有webapp的类加载器隔离,然后配置该类加载器隔离层以通过此隔离戳出一个小洞来实现此目的。
这个洞需要:
这很容易在嵌入式码头中完成。
让我们从创建com.company.foo.TestServlet
的自定义版本开始,并使其在嵌入式jetty服务器的类路径中可用。
最后,配置您的WebAppContext
以配置上面提到的类加载器隔离孔。 (使用系统类和服务器类配置)
package demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
public class EmbedWithOverridenServlet
{
public static void main(String[] args) throws Exception
{
int port = 8080;
Server server = new Server(port);
String wardir = "path/to/example-webapp.war";
WebAppContext webapp = new WebAppContext();
webapp.setWar(wardir);
webapp.setContextPath("/");
// don't hide server classes from webapps (hole #1 from answer)
// (allow webapp to use ones from system classloader)
webapp.addServerClass("-com.company.foo.");
// webapp cannot change or replace these classes
// force use of server classes (hole #2 from answer)
webapp.addSystemClass("com.company.foo.");
server.setHandler(webapp);
try
{
server.start();
server.dumpStdErr();
makeWebRequest(new URI("http://localhost:8080/test"));
}
finally
{
server.stop();
}
}
private static void makeWebRequest(URI uri) throws IOException
{
HttpURLConnection conn = (HttpURLConnection)uri.toURL().openConnection();
conn.setAllowUserInteraction(false);
int status = conn.getResponseCode();
System.out.printf("Response Status: %d%n", status);
if(status != 200)
{
System.out.printf("ERROR: %s%n", conn.getResponseMessage());
return;
}
try(InputStream in = conn.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
BufferedReader buf = new BufferedReader(reader))
{
System.out.printf("Response Content Type: %s%n", conn.getHeaderField("Content-Type"));
String line;
while ((line = buf.readLine()) != null)
{
System.out.printf("[resp] %s%n",line);
}
}
}
}
更新:2014年10月16日
可在github.com/jetty-project/jetty-classloader-manipulation
上找到展示此项目的项目 example-webapp
是一个非常正常的网络应用,single servlet为its own。
example-embedded-servlet-override
有一个replacement for that servlet,还有embedded jetty main class来部署example-webapp
。它有system property check against use.server.class
来演示行为,正常(孤立)和重写(打孔)。
输出如下:
正常/孤立行为:(无系统道具或使用-Duse.server.class=false
)
Response Status: 200
Response Content Type: text/plain; charset=ISO-8859-1
[resp] Hello Test Class: com.company.foo.TestServlet
from ClassLoader WebAppClassLoader=2113433461@7df86f75
重写/打孔行为:(使用-Duse.server.class=true
)
Response Status: 200
Response Content Type: text/html; charset=ISO-8859-1
[resp] <html><body>
[resp] <h1>Hello Server Impl</h1>
[resp] <p>Class: com.company.foo.TestServlet
from ClassLoader sun.misc.Launcher$AppClassLoader@63e68a2b</p>
[resp] </body></html>
希望这可以帮助您了解您正在使用的内容的性质。
答案 2 :(得分:0)
我只能假设有问题的类存在于WEB-INF / lib或WEB-INF / classes中。不知道你为什么要确保webapp无法找到这个类,但是尝试使用不是WebAppClassLoader实例的类加载器,就像一个带有空URL列表的新URLClassLoader和rootLoader作为其父级。
扬