嵌入式码头服务器启动后立即初始化 servlet
Init servlet instantly after the embedded jetty server starts
我需要在 Jetty 嵌入式服务器启动后 运行 我自己的逻辑。由于 classloader 问题,我没有从主 class 启动它。一个理想的解决方案似乎是 运行 从 servlet 初始化中整合我的服务器逻辑。但是 init 函数和构造函数在码头服务器启动后不会被调用。在第一个 HTTP 请求期间正在创建 servlet 的实例。是否可以告诉 jetty 立即初始化我的 servlet,或者我真的需要用我的自定义 classloader 加载所有 classes 然后启动 jetty 服务器?
这是主要的class:
public class ServerLauncher {
public static void main(String[] args) {
JettyServerLauncher.launchHttp("target/server.war", "0.0.0.0", 8080);
// Starting my own logic here is causing classloader issues, because WebSocket classes are loaded by other classloader than my classes, that is the reason why I moved it into the servlet
}
}
这是我的 Jetty 嵌入式服务器启动器:
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.*;
import java.io.File;
public class JettyServerLauncher {
private static boolean isHttps;
private static File keyStoreFile;
private static String warPath;
private static String host;
private static int httpPort;
private static int httpsPort;
private static String keyStorePath;
private static String keyStorePass;
private static boolean needClientAuth;
public static void launchHttp(String warPath, String host, int httpPort) {
JettyServerLauncher.isHttps = false;
JettyServerLauncher.warPath = warPath;
JettyServerLauncher.host = host;
JettyServerLauncher.httpPort = httpPort;
launch();
}
public static void launchHttps(String warPath, String host, String keyStorePath, String keyStorePass, int httpPort, int httpsPort, boolean needClientAuth) {
JettyServerLauncher.isHttps = true;
JettyServerLauncher.warPath = warPath;
JettyServerLauncher.host = host;
JettyServerLauncher.httpPort = httpPort;
JettyServerLauncher.httpsPort = httpsPort;
JettyServerLauncher.keyStorePath = keyStorePath;
JettyServerLauncher.keyStorePass = keyStorePass;
JettyServerLauncher.needClientAuth = needClientAuth;
launch();
}
private static void launch() {
Server server = null;
try {
System.out.println("Initializing jetty server...");
if (isHttps) loadKeyStores(keyStorePath);
// Create jetty server
server = new Server(httpPort);
// Setup connectors
Connector httpConnector = createHttpConnector(server, host, httpPort, httpsPort);
if (isHttps) {
Connector httpsConnector = createHttpsConnector(server, host, httpsPort, keyStoreFile, keyStorePass, needClientAuth);
server.setConnectors(new Connector[]{httpConnector, httpsConnector});
} else {
server.setConnectors(new Connector[]{httpConnector});
}
// Add handlers for requests to collection of handlers
HandlerCollection handlers = new ContextHandlerCollection();
//handlers.addHandler(new SecuredRedirectHandler());
handlers.addHandler(createWebApp(warPath));
server.setHandler(handlers);
server.dump();
System.out.println("Starting jetty websocket and web server...");
server.start();
server.join();
} catch (Throwable t) {
t.printStackTrace();
System.err.println("Server initialization failed!");
System.out.println("Stopping the server...");
try {
server.stop();
} catch (Exception ignored) {}
}
}
private static WebAppContext createWebApp(String warPath) {
WebAppContext webApp = new WebAppContext();
webApp.setContextPath("/");
webApp.setWar(new File(warPath).getAbsolutePath());
webApp.setThrowUnavailableOnStartupException(true);
// Enable support for JSR-356 javax.websocket
webApp.setAttribute("org.eclipse.jetty.websocket.jsr356", Boolean.TRUE);
// Jetty will scan project for configuration files... This is very important for loading websocket endpoints by annotation automatically
webApp.setConfigurations(new Configuration[] {
new AnnotationConfiguration(),
new WebInfConfiguration(),
new WebXmlConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new JettyWebXmlConfiguration()
});
return webApp;
}
private static Connector createHttpConnector(Server server, String host, int httpPort, int httpsPort) {
HttpConfiguration httpConf = new HttpConfiguration();
httpConf.setSendServerVersion(false);
if (isHttps) httpConf.setSecurePort(httpsPort);
ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConf));
connector.setPort(httpPort);
connector.setHost(host);
return connector;
}
private static Connector createHttpsConnector(Server server, String host, int httpsPort, File keyStoreFile, String keyStorePass, boolean needClientAuth) {
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(keyStoreFile.getAbsolutePath());
sslContextFactory.setKeyStorePassword(keyStorePass);
sslContextFactory.setNeedClientAuth(needClientAuth);
// Setup HTTPS Configuration
HttpConfiguration httpsConf = new HttpConfiguration();
httpsConf.setSendServerVersion(false);
httpsConf.setSecureScheme("https");
httpsConf.setSecurePort(httpsPort);
httpsConf.setOutputBufferSize(32768);
httpsConf.setRequestHeaderSize(8192);
httpsConf.setResponseHeaderSize(8192);
httpsConf.addCustomizer(new SecureRequestCustomizer()); // adds ssl info to request object
// Establish the HTTPS ServerConnector
ServerConnector httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(httpsConf));
httpsConnector.setPort(httpsPort);
httpsConnector.setHost(host);
return httpsConnector;
}
private static void loadKeyStores(String keyStorePath) {
keyStoreFile = new File(keyStorePath);
if (!keyStoreFile.exists()) {
throw new RuntimeException("Key store file does not exist on path '"+keyStoreFile.getAbsolutePath()+"'");
}
}
}
这是我的 servlet:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(displayName = "MyServlet", urlPatterns = { "/*" })
public class MyServlet extends HttpServlet {
@Override
public void init() {
// start new Thread with my server logic here (avoid classloader issues)
// but at least one HTTP request is needed to start it from this place
}
@Override
public void destroy() {}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
// handle http requests
}
}
我在 google 上找到了这个,但我不知道如何在我的案例中使用它。 https://www.eclipse.org/lists/jetty-users/msg02109.html
感谢您的帮助。
如果你只是想让 servlet 在启动时初始化,那么使用注释 ...
@WebServlet(
displayName = "MyServlet",
urlPatterns = { "/*" },
loadOnStartup = 1
)
或者,您可以注册一个 javax.servlet.ServletContextListener
来执行您需要的 contextInitialized(ServletContextEvent sce)
行为。
提示:如果您为嵌入使用定义自定义 ServletContextListener
,您可以将它从您正在使用的 WAR 外部添加到 WebAppContext
。
示例:
webApp.getServletHandler()
.addListener(new ListenerHolder(MyContextListener.class));
此外,此代码块是错误的,它向您显示 copy/pasted 来自旧代码片段(此技术大约来自 Jetty 9.0.0 至 9.2.16)
webApp.setConfigurations(new Configuration[] {
new AnnotationConfiguration(),
new WebInfConfiguration(),
new WebXmlConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new JettyWebXmlConfiguration()
});
在 Jetty 9.4.x 中,您永远不会像那样直接配置 webApp.setConfigurations()
,而是使用服务器上定义的 Configuration.ClassList
...
发件人:9.4.44.v20210927 - embedded/LikeJettyXml.java
Configuration.ClassList classlist = Configuration.ClassList
.setServerDefault(server);
classlist.addAfter(
"org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.plus.webapp.EnvConfiguration",
"org.eclipse.jetty.plus.webapp.PlusConfiguration");
classlist.addBefore(
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
"org.eclipse.jetty.annotations.AnnotationConfiguration");
从 Jetty 10.0.0 开始,您永远不会指定配置 类 或它们的顺序,因为支持 JAR 的存在就足够了,并且在 Jetty 10 内部顺序被正确解析。
但是如果您需要添加配置(由于 non-standard 部署问题 Java ServiceLoader
不起作用),那么您仍然需要在服务器上配置额外的配置对象(但不用担心这些配置的正确顺序)
来自10.0.7 - embedded/demos/LikeJettyXml.java
Configurations.setServerDefault(server).add(
new EnvConfiguration(),
new PlusConfiguration(),
new AnnotationConfiguration()
);
我需要在 Jetty 嵌入式服务器启动后 运行 我自己的逻辑。由于 classloader 问题,我没有从主 class 启动它。一个理想的解决方案似乎是 运行 从 servlet 初始化中整合我的服务器逻辑。但是 init 函数和构造函数在码头服务器启动后不会被调用。在第一个 HTTP 请求期间正在创建 servlet 的实例。是否可以告诉 jetty 立即初始化我的 servlet,或者我真的需要用我的自定义 classloader 加载所有 classes 然后启动 jetty 服务器?
这是主要的class:
public class ServerLauncher {
public static void main(String[] args) {
JettyServerLauncher.launchHttp("target/server.war", "0.0.0.0", 8080);
// Starting my own logic here is causing classloader issues, because WebSocket classes are loaded by other classloader than my classes, that is the reason why I moved it into the servlet
}
}
这是我的 Jetty 嵌入式服务器启动器:
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.*;
import java.io.File;
public class JettyServerLauncher {
private static boolean isHttps;
private static File keyStoreFile;
private static String warPath;
private static String host;
private static int httpPort;
private static int httpsPort;
private static String keyStorePath;
private static String keyStorePass;
private static boolean needClientAuth;
public static void launchHttp(String warPath, String host, int httpPort) {
JettyServerLauncher.isHttps = false;
JettyServerLauncher.warPath = warPath;
JettyServerLauncher.host = host;
JettyServerLauncher.httpPort = httpPort;
launch();
}
public static void launchHttps(String warPath, String host, String keyStorePath, String keyStorePass, int httpPort, int httpsPort, boolean needClientAuth) {
JettyServerLauncher.isHttps = true;
JettyServerLauncher.warPath = warPath;
JettyServerLauncher.host = host;
JettyServerLauncher.httpPort = httpPort;
JettyServerLauncher.httpsPort = httpsPort;
JettyServerLauncher.keyStorePath = keyStorePath;
JettyServerLauncher.keyStorePass = keyStorePass;
JettyServerLauncher.needClientAuth = needClientAuth;
launch();
}
private static void launch() {
Server server = null;
try {
System.out.println("Initializing jetty server...");
if (isHttps) loadKeyStores(keyStorePath);
// Create jetty server
server = new Server(httpPort);
// Setup connectors
Connector httpConnector = createHttpConnector(server, host, httpPort, httpsPort);
if (isHttps) {
Connector httpsConnector = createHttpsConnector(server, host, httpsPort, keyStoreFile, keyStorePass, needClientAuth);
server.setConnectors(new Connector[]{httpConnector, httpsConnector});
} else {
server.setConnectors(new Connector[]{httpConnector});
}
// Add handlers for requests to collection of handlers
HandlerCollection handlers = new ContextHandlerCollection();
//handlers.addHandler(new SecuredRedirectHandler());
handlers.addHandler(createWebApp(warPath));
server.setHandler(handlers);
server.dump();
System.out.println("Starting jetty websocket and web server...");
server.start();
server.join();
} catch (Throwable t) {
t.printStackTrace();
System.err.println("Server initialization failed!");
System.out.println("Stopping the server...");
try {
server.stop();
} catch (Exception ignored) {}
}
}
private static WebAppContext createWebApp(String warPath) {
WebAppContext webApp = new WebAppContext();
webApp.setContextPath("/");
webApp.setWar(new File(warPath).getAbsolutePath());
webApp.setThrowUnavailableOnStartupException(true);
// Enable support for JSR-356 javax.websocket
webApp.setAttribute("org.eclipse.jetty.websocket.jsr356", Boolean.TRUE);
// Jetty will scan project for configuration files... This is very important for loading websocket endpoints by annotation automatically
webApp.setConfigurations(new Configuration[] {
new AnnotationConfiguration(),
new WebInfConfiguration(),
new WebXmlConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new JettyWebXmlConfiguration()
});
return webApp;
}
private static Connector createHttpConnector(Server server, String host, int httpPort, int httpsPort) {
HttpConfiguration httpConf = new HttpConfiguration();
httpConf.setSendServerVersion(false);
if (isHttps) httpConf.setSecurePort(httpsPort);
ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConf));
connector.setPort(httpPort);
connector.setHost(host);
return connector;
}
private static Connector createHttpsConnector(Server server, String host, int httpsPort, File keyStoreFile, String keyStorePass, boolean needClientAuth) {
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(keyStoreFile.getAbsolutePath());
sslContextFactory.setKeyStorePassword(keyStorePass);
sslContextFactory.setNeedClientAuth(needClientAuth);
// Setup HTTPS Configuration
HttpConfiguration httpsConf = new HttpConfiguration();
httpsConf.setSendServerVersion(false);
httpsConf.setSecureScheme("https");
httpsConf.setSecurePort(httpsPort);
httpsConf.setOutputBufferSize(32768);
httpsConf.setRequestHeaderSize(8192);
httpsConf.setResponseHeaderSize(8192);
httpsConf.addCustomizer(new SecureRequestCustomizer()); // adds ssl info to request object
// Establish the HTTPS ServerConnector
ServerConnector httpsConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(httpsConf));
httpsConnector.setPort(httpsPort);
httpsConnector.setHost(host);
return httpsConnector;
}
private static void loadKeyStores(String keyStorePath) {
keyStoreFile = new File(keyStorePath);
if (!keyStoreFile.exists()) {
throw new RuntimeException("Key store file does not exist on path '"+keyStoreFile.getAbsolutePath()+"'");
}
}
}
这是我的 servlet:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(displayName = "MyServlet", urlPatterns = { "/*" })
public class MyServlet extends HttpServlet {
@Override
public void init() {
// start new Thread with my server logic here (avoid classloader issues)
// but at least one HTTP request is needed to start it from this place
}
@Override
public void destroy() {}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
// handle http requests
}
}
我在 google 上找到了这个,但我不知道如何在我的案例中使用它。 https://www.eclipse.org/lists/jetty-users/msg02109.html
感谢您的帮助。
如果你只是想让 servlet 在启动时初始化,那么使用注释 ...
@WebServlet(
displayName = "MyServlet",
urlPatterns = { "/*" },
loadOnStartup = 1
)
或者,您可以注册一个 javax.servlet.ServletContextListener
来执行您需要的 contextInitialized(ServletContextEvent sce)
行为。
提示:如果您为嵌入使用定义自定义 ServletContextListener
,您可以将它从您正在使用的 WAR 外部添加到 WebAppContext
。
示例:
webApp.getServletHandler()
.addListener(new ListenerHolder(MyContextListener.class));
此外,此代码块是错误的,它向您显示 copy/pasted 来自旧代码片段(此技术大约来自 Jetty 9.0.0 至 9.2.16)
webApp.setConfigurations(new Configuration[] {
new AnnotationConfiguration(),
new WebInfConfiguration(),
new WebXmlConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new JettyWebXmlConfiguration()
});
在 Jetty 9.4.x 中,您永远不会像那样直接配置 webApp.setConfigurations()
,而是使用服务器上定义的 Configuration.ClassList
...
发件人:9.4.44.v20210927 - embedded/LikeJettyXml.java
Configuration.ClassList classlist = Configuration.ClassList
.setServerDefault(server);
classlist.addAfter(
"org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.plus.webapp.EnvConfiguration",
"org.eclipse.jetty.plus.webapp.PlusConfiguration");
classlist.addBefore(
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
"org.eclipse.jetty.annotations.AnnotationConfiguration");
从 Jetty 10.0.0 开始,您永远不会指定配置 类 或它们的顺序,因为支持 JAR 的存在就足够了,并且在 Jetty 10 内部顺序被正确解析。
但是如果您需要添加配置(由于 non-standard 部署问题 Java ServiceLoader
不起作用),那么您仍然需要在服务器上配置额外的配置对象(但不用担心这些配置的正确顺序)
来自10.0.7 - embedded/demos/LikeJettyXml.java
Configurations.setServerDefault(server).add(
new EnvConfiguration(),
new PlusConfiguration(),
new AnnotationConfiguration()
);