com.jfinal.server.jetty.JettyServer Maven / Gradle / Ivy
/**
* Copyright (c) 2011-2019, James Zhan 詹波 ([email protected]).
*
* Licensed 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 com.jfinal.server.jetty;
import java.io.File;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.session.FileSessionDataStoreFactory;
import org.eclipse.jetty.webapp.WebAppContext;
import com.jfinal.core.Const;
import com.jfinal.kit.FileKit;
import com.jfinal.kit.LogKit;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.StrKit;
import com.jfinal.server.IServer;
/**
* JettyServer is used to config and start jetty web server.
* Jetty version 9.4.12.v20180830
*/
class JettyServer implements IServer {
protected String webAppDir;
protected int port;
protected String context;
protected int scanIntervalSeconds;
protected Server server;
protected WebAppContext webApp;
protected volatile boolean started = false;
JettyServer(String webAppDir, int port, String context, int scanIntervalSeconds) {
if (webAppDir == null) {
throw new IllegalStateException("Invalid webAppDir of web server: " + webAppDir);
}
if (port < 0 || port > 65535) {
throw new IllegalArgumentException("Invalid port of web server: " + port);
}
if (StrKit.isBlank(context)) {
throw new IllegalStateException("Invalid context of web server: " + context);
}
this.webAppDir = webAppDir;
this.port = port;
this.context = context;
this.scanIntervalSeconds = scanIntervalSeconds;
}
public void start() {
if (!started) {
try {
started = true;
doStart();
} catch (Exception e) {
System.err.println(e.getMessage());
LogKit.error(e.getMessage(), e);
}
}
}
public void stop() {
if (started) {
try {server.stop();} catch (Exception e) {LogKit.error(e.getMessage(), e);}
started = false;
}
}
private void doStart() {
if (!available(port)) {
throw new IllegalStateException("port: " + port + " already in use!");
}
deleteSessionData();
System.out.println("Starting JFinal " + Const.JFINAL_VERSION);
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(port);
server.addConnector(connector);
webApp = new WebAppContext();
// webApp.addSystemClass("sun.");
// webApp.addSystemClass("com.sun.");
webApp.getSystemClasspathPattern().add("sun."); // 支持 IDEA
webApp.getSystemClasspathPattern().add("com.sun.");
// webApp.getSystemClasspathPattern().add("org.apache."); // 支持 jsp
// webApp.getSystemClasspathPattern().add("org.glassfish."); // 支持 jsp
webApp.getSystemClasspathPattern().add("org.apache.jasper."); // 支持 jsp,不影响 org.apache.shiro
webApp.getSystemClasspathPattern().add("org.apache.taglibs."); // 支持 jsp,不影响 org.apache.shiro
webApp.getSystemClasspathPattern().add("org.glassfish.jsp."); // 支持 jsp
webApp.getSystemClasspathPattern().add("org.slf4j."); // 支持slf4j
webApp.setThrowUnavailableOnStartupException(true); // 在启动过程中允许抛出异常终止启动并退出 JVM
webApp.setContextPath(context);
webApp.setResourceBase(webAppDir); // webApp.setWar(webAppDir);
webApp.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
webApp.setInitParameter("org.eclipse.jetty.servlet.Default.useFileMappedBuffer", "false"); // webApp.setInitParams(Collections.singletonMap("org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false"));
webApp.setMaxFormContentSize(256 * 1024 * 1024);
persistSession(server); // persistSession(webApp);
server.setHandler(webApp);
server.setStopAtShutdown(true);
server.setDumpAfterStart(false);
server.setDumpBeforeStop(false);
// configureScanner
if (scanIntervalSeconds > 0) {
//only need to change classloader when scanIntervalSeconds > 0
changeClassLoader(webApp);
Scanner scanner = new Scanner(PathKit.getRootClassPath(), scanIntervalSeconds) {
public void onChange() {
try {
System.err.println("\nLoading changes ......");
webApp.stop();
JFinalClassLoader loader = new JFinalClassLoader(webApp, getClassPath());
webApp.setClassLoader(loader);
webApp.start();
System.err.println("Loading complete (^_^)");
} catch (Exception e) {
System.err.println("Error reconfiguring/restarting webapp after change in watched files");
LogKit.error(e.getMessage(), e);
}
}
};
System.out.println("Starting scanner at interval of " + scanIntervalSeconds + " seconds");
scanner.start();
}
try {
System.out.println("Starting Jetty Server " + Server.getVersion() + " on port: " + port);
server.start();
System.out.println("Starting Complete. Welcome To The JFinal World (^_^)");
server.join();
} catch (Exception e) {
LogKit.error(e.getMessage(), e);
System.exit(100);
}
return;
}
private void changeClassLoader(WebAppContext webApp) {
try {
String classPath = getClassPath();
JFinalClassLoader jfcl = new JFinalClassLoader(webApp, classPath);
// jfcl.addClassPath(classPath);
webApp.setClassLoader(jfcl);
} catch (IOException e) {
LogKit.error(e.getMessage(), e);
}
}
private String getClassPath() {
return System.getProperty("java.class.path");
}
private void deleteSessionData() {
try {
FileKit.delete(new File(getStoreDir()));
}
catch (Exception e) {
LogKit.logNothing(e);
}
}
private String getStoreDir() {
String storeDir = PathKit.getWebRootPath() + "/../../session_data" + context;
if ("\\".equals(File.separator)) {
storeDir = storeDir.replaceAll("/", "\\\\");
}
return storeDir;
}
private void persistSession(Server server) {
String storeDir = getStoreDir();
FileSessionDataStoreFactory fsdsf = new FileSessionDataStoreFactory();
fsdsf.setStoreDir(new File(storeDir));
server.addBean(fsdsf);
}
/* 改用 server.addBean(fsdsf) 实现
private void persistSession(WebAppContext webApp) {
// http://www.eclipse.org/jetty/documentation/current/session-management.html
String storeDir = getStoreDir();
SessionCache sc = webApp.getSessionHandler().getSessionCache();
if (sc != null) {
SessionDataStore sds = sc.getSessionDataStore();
if (sds instanceof FileSessionDataStore) {
FileSessionDataStore fsd = (FileSessionDataStore)sds;
fsd.setStoreDir(new File(storeDir));
return ;
}
}
SessionHandler sessionHandler = webApp.getSessionHandler();
if (sessionHandler == null) {
sessionHandler = new SessionHandler();
webApp.setSessionHandler(sessionHandler);
}
FileSessionDataStoreFactory fsdsf = new FileSessionDataStoreFactory();
fsdsf.setStoreDir(new File(storeDir));
SessionDataStore sessionDataStore = fsdsf.getSessionDataStore(sessionHandler);
DefaultSessionCacheFactory dscf = new DefaultSessionCacheFactory();
SessionCache sessionCache = dscf.getSessionCache(sessionHandler);
sessionCache.setSessionDataStore(sessionDataStore);
sessionHandler.setSessionCache(sessionCache);
} */
private static boolean available(int port) {
if (port <= 0) {
throw new IllegalArgumentException("Invalid start port: " + port);
}
ServerSocket ss = null;
DatagramSocket ds = null;
try {
ss = new ServerSocket(port);
ss.setReuseAddress(true);
ds = new DatagramSocket(port);
ds.setReuseAddress(true);
return true;
} catch (IOException e) {
LogKit.logNothing(e);
} finally {
if (ds != null) {
ds.close();
}
if (ss != null) {
try {
ss.close();
} catch (IOException e) {
// should not be thrown, just detect port available.
LogKit.logNothing(e);
}
}
}
return false;
}
}