文档章节

升级JFinal内置的jetty到9版本,并且支持ssl

玛雅牛
 玛雅牛
发布于 2015/06/24 10:39
字数 890
阅读 3844
收藏 15

JFinal本事内置的jetty8已经基本满足开发需求。由于本人是版本控,同时想在某些小型项目中直接用内置的jetty运行。

于是将内置的jetty升级到9.由于jetty9盒jetty8变化很大。所以几乎需要重写com.jfinal.server.JettyServer的doStart方法。需要引入jetty9的jar包。

最新的Jetty 9.3.0.v20150612 最低要求java8

代码如下:

/**
 * Copyright (c) 2011-2015, James Zhan 詹波 (jfinal@126.com).
 *
 * 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;

import java.io.File;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.util.EnumSet;

import javax.servlet.DispatcherType;

import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SessionManager;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.session.HashSessionManager;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.WebAppContext;

import com.jfinal.core.Const;
import com.jfinal.kit.FileKit;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.StrKit;

/**
 * JettyServer is used to config and start jetty web server.
 * Jetty version 8.1.8
 */
class JettyServer implements IServer {
	
	private String webAppDir;
	private int port;
	private String context;
	private int scanIntervalSeconds;
	private boolean running = false;
	private Server server;
	private WebAppContext webApp;
	
	private String keyStorePath = null;
	private String keyStorePassword = null;
	private String keyManagerPassword = null;
	
	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 > 65536)
			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;
	}
	
	JettyServer(String webAppDir, int port, String context, String keyStorePath,String keyStorePassword,String keyManagerPassword) {
		this(webAppDir,port,context,0);
		this.keyManagerPassword = keyManagerPassword;
		this.keyStorePassword = keyStorePassword;
		this.keyStorePath = keyStorePath;
	}
	
	public void start() {
		if (!running) {
			try {doStart();} catch (Exception e) {e.printStackTrace();}
			running = true;
		}
	}
	
	public void stop() {
		if (running) {
			try {webApp.stop();server.stop();} catch (Exception e) {e.printStackTrace();}
			running = 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();
		
		//httl配置。
		if(null == this.keyStorePath){
			HttpConfiguration http_config = new HttpConfiguration();	        // HTTP connector
	        ServerConnector connector = new ServerConnector(server,new HttpConnectionFactory(http_config));
			connector.setReuseAddress(true);
			connector.setIdleTimeout(30000);
			
			connector.setPort(port);
			server.addConnector(connector);
		}else{
			//https 配置
			HttpConfiguration https_config = new HttpConfiguration();
			https_config.setSecureScheme("https");
			https_config.setSecurePort(port);
			https_config.setOutputBufferSize(32768);
			https_config.addCustomizer(new SecureRequestCustomizer());
			SslContextFactory sslContextFactory = new SslContextFactory();
	        sslContextFactory.setKeyStorePath(this.keyStorePath);
	        sslContextFactory.setKeyStorePassword(this.keyStorePassword);
	        sslContextFactory.setKeyManagerPassword(this.keyManagerPassword);
	        ServerConnector httpsConnector = new ServerConnector(server,
	                new SslConnectionFactory(sslContextFactory,"http/1.1"),
	                new HttpConnectionFactory(https_config));
	        httpsConnector.setPort(port);
	        httpsConnector.setIdleTimeout(500000);
	        server.addConnector(httpsConnector);
		}
		
		webApp = new WebAppContext();		
	    /**
		 * 增加gzip支持
		 */
		FilterHolder  fh = new FilterHolder();
		fh.setAsyncSupported(true);
		fh.setClassName("org.eclipse.jetty.servlets.GzipFilter");
		fh.setInitParameter("mimeTypes", "text/html,text/plain,text/xml,text/css,text/javascript,application/javascript,image/gif,image/png");
		EnumSet<DispatcherType> set = EnumSet.noneOf(DispatcherType.class);
		set.add(DispatcherType.REQUEST);
		set.add(DispatcherType.FORWARD);
		set.add(DispatcherType.INCLUDE);
		set.add(DispatcherType.ERROR);
		set.add(DispatcherType.ASYNC);
		webApp.addFilter(fh, "/*", set);
		
		
		webApp.setContextPath(context);
		webApp.setResourceBase(webAppDir);
		webApp.setMaxFormContentSize(81920000);
		webApp.getInitParams().put("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
		webApp.getInitParams().put("org.eclipse.jetty.servlet.Default.useFileMappedBuffer", "true");
		webApp.getInitParams().put("org.eclipse.jetty.server.Request.maxFormContentSize", "-1");
		
		persistSession(webApp);
		
		server.setHandler(webApp);
		changeClassLoader(webApp);
		
		// configureScanner
		if (scanIntervalSeconds > 0) {
			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");
						e.printStackTrace();
					}
				}
			};
			System.out.println("Starting scanner at interval of " + scanIntervalSeconds + " seconds.");
			scanner.start();
		}
		
		try {
			System.out.println("Starting web server on port: " + port);
			server.start();
			System.out.println("Starting Complete. Welcome To The JFinal World :)");
			server.join();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(100);
		}
		return;
	}
	
	@SuppressWarnings("resource")
	private void changeClassLoader(WebAppContext webApp) {
		try {
			String classPath = getClassPath();
			JFinalClassLoader wacl = new JFinalClassLoader(webApp, classPath);
			wacl.addClassPath(classPath);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private String getClassPath() {
		return System.getProperty("java.class.path");
	}
	
	private void deleteSessionData() {
		try {
			FileKit.delete(new File(getStoreDir()));
		}
		catch (Exception e) {
		}
	}
	
	private String getStoreDir() {
		String storeDir = PathKit.getWebRootPath() + "/../../session_data" + context;
		if ("\\".equals(File.separator))
			storeDir = storeDir.replaceAll("/", "\\\\");
		return storeDir;
	}
	
	private void persistSession(WebAppContext webApp) {
		String storeDir = getStoreDir();
		
		SessionManager sm = webApp.getSessionHandler().getSessionManager();
		if (sm instanceof HashSessionManager) {
			try {
				((HashSessionManager)sm).setStoreDirectory(new File(storeDir));
			} catch (IOException e) {
				e.printStackTrace();
			}
			return ;
		}
		
		HashSessionManager hsm = new HashSessionManager();
		try {
			hsm.setStoreDirectory(new File(storeDir));
		} catch (IOException e) {
			e.printStackTrace();
		}
		SessionHandler sh = new SessionHandler();
		sh.setSessionManager(hsm);
		webApp.setSessionHandler(sh);
	}
	
	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) {
		} finally {
			if (ds != null) {
				ds.close();
			}
			
			if (ss != null) {
				try {
					ss.close();
				} catch (IOException e) {
					// should not be thrown, just detect port available.
				}
			}
		}
		return false;
	}
}

为了支持ssl还得修改com.jfinal.core.JFinal实现,增加相应的方法。

public static void startSSLServer(String webAppDir, int port, String context, String keyStorePath,String keyStorePassword,String keyManagerPassword) {
		server = ServerFactory.getHttpsServer(webAppDir, port, context, keyStorePath, keyStorePassword, keyManagerPassword);
		server.start();
	}

jetty-all下载地址

http://repo1.maven.org/maven2/org/eclipse/jetty/aggregate/jetty-all/


© 著作权归作者所有

玛雅牛

玛雅牛

粉丝 489
博文 114
码字总数 28117
作品 4
西安
高级程序员
私信 提问
加载中

评论(13)

-启曙-
-启曙-
jetty9的start方法是带参数的,并且是protected。这里的代码能跑起来吗
Jimmy哥
Jimmy哥

引用来自“Jimmy哥”的评论

环境:jetty8 + jdk8
jetty:run项目会ArrayIndexOutOfBoundsException抛异常

引用来自“玛雅牛”的评论

我这是jetty9,jetty8+jdk8没有试过。
感谢回复! jfinal项目最先使用jetty8+jdk8,部署到服务器会抛: Caused by: java.lang.ArrayIndexOutOfBoundsException: 6241 at org.objectweb.asm.ClassReader.(Unknown Source) at org.objectweb.asm.ClassReader.(Unknown Source) at org.objectweb.asm.ClassReader.(Unknown Source) 应该是这个bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=419801 升jetty9之后(jdk不变),部署一切正常
玛雅牛
玛雅牛 博主

引用来自“Jimmy哥”的评论

环境:jetty8 + jdk8
jetty:run项目会ArrayIndexOutOfBoundsException抛异常
我这是jetty9,jetty8+jdk8没有试过。
Jimmy哥
Jimmy哥
环境:jetty8 + jdk8
jetty:run项目会ArrayIndexOutOfBoundsException抛异常
暴走1
暴走1
另外能不能給出 getHttpsServer 方法的實現啊 ^^
暴走1
暴走1
不知道能不能有個過分的請求 @JFinal@玛雅牛 ,希望能 fork一個版本出來,編譯好bin。版本控加伸手黨,目前java還不會編譯源碼為jar,所以...
i刘晓伟
i刘晓伟

引用来自“孤独的3”的评论

13版本控太多了~

赞··
玛雅牛
玛雅牛 博主

引用来自“JFinal”的评论

jfinal 因为要顾及到 jdk 1.6、1.7 的开发者,所以 jetty 一直没升级

引用来自“玛雅牛”的评论

嗯。我们是直接全面拥抱java8,所以版本不是问题。对应还坚守用1.6,1.7的目前还是用不了。

引用来自“JFinal”的评论

jfinal 太多用户了,需要照顾到很多人,没办法
对于开发测试来说jetty8是够了。
JFinal
JFinal

引用来自“JFinal”的评论

jfinal 因为要顾及到 jdk 1.6、1.7 的开发者,所以 jetty 一直没升级

引用来自“玛雅牛”的评论

嗯。我们是直接全面拥抱java8,所以版本不是问题。对应还坚守用1.6,1.7的目前还是用不了。
jfinal 太多用户了,需要照顾到很多人,没办法
玛雅牛
玛雅牛 博主

引用来自“JFinal”的评论

jfinal 因为要顾及到 jdk 1.6、1.7 的开发者,所以 jetty 一直没升级
嗯。我们是直接全面拥抱java8,所以版本不是问题。对应还坚守用1.6,1.7的目前还是用不了。
JFinal 3.5 发布,将能上的菜先上了

jfinal 新功能经过 6 个月的酝酿、开发,在大幅度的创新来临之前,jfinal 3.5 这一版先稳一稳,趁着国庆长假,将能上的菜先上了。 jfinal 3.5 这一版针对这 6 个月以来用户反馈最强烈、最频繁...

JFinal
2018/10/08
12.3K
101
JFinal Undertow 1.4 发布,小版本迭代

JFinal Undertow 项目在公司内部试用两个月,在俱乐部内测一个月,在 2018-12-12 号才放出 jfinal undertow 1.3 版本,这个版本已经十分完善好用 jfinal underow 1.4 针对自 1.3 发布以来的一...

JFinal
2018/12/25
1K
24
JFinal 1.8 发布,JAVA 极速 WEB+ORM 框架

JFinal 1.8 主要改进了Db + Record设计,对多数据源支持有大幅度的提升。相对于 1.6 版,多数据源设计在语义上更加明确,之前缺失的 API 也全部被补充进来,API 的使用方式与单数据源使用时保...

JFinal
2014/05/23
16.6K
158
JFinal 1.2 发布,JAVA极速WEB+ORM框架

转间眼JFinal开源已经十个月了,在过去的一年里JFinal经历了12次版本更新,在保持了微内核架构的同时持续完善功能、提升开发体验。不知道大家是否正在使用JFinal做项目,是否体验到了极速开发...

JFinal
2013/01/28
5.1K
73
JFinal 3.6 发布,五弹齐发迎新年

春节前处理完年前用户反馈的需求与改进,来年只需专注面向未来。 jfinal 3.6 有近 40 项增强与改进,部分功能与改进十分有价值,值得你拥有。 一、第一弹 JFinal 3.6 1、Controller 添加 se...

JFinal
02/01
2.8K
25

没有更多内容

加载失败,请刷新页面

加载更多

程序设计基础(C)第06讲例程

1summing.c /* summing.c -- 根据用户键入的整数求和 */#include <stdio.h>int main(void){ long num; long sum = 0L; /* 把sum 初始化为0 */ int status; p......

树人大学数字媒体吴凡
15分钟前
4
0
聊聊nacos config的publishConfig

序 本文主要研究一下nacos config的publishConfig ConfigController nacos-1.1.3/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java @Controller@R......

go4it
42分钟前
3
0
Eureka应用注册与集群数据同步源码解析

在之前的EurekaClient自动装配及启动流程解析一文中我们提到过,在构造DiscoveryClient类时,会把自身注册到服务端,本文就来分析一下这个注册流程 客户端发起注册 boolean register() t...

Java学习录
52分钟前
11
0
Java描述设计模式(15):责任链模式

本文源码:GitHub·点这里 || GitEE·点这里 一、生活场景描述 1、请假审批流程 公司常见的请假审批流程:请假天数 当 day<=3 天,项目经理审批当 3<day<=5 天,部门经理审批当 day>5 天...

知了一笑
今天
10
0
总结:数组与链表

1、内存申请:数组在内存上是连续的空间;链表,内存地址上可以是不连续的。 2、查询速度:数组可以随机访问,链表必须顺序访问,即从首个元素开始遍历,逐个查找,所以数组查询很快。 3、写入...

浮躁的码农
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部