/**
* Copyright (c) 2011-2012, 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.lang.management.ManagementFactory;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.management.MBeanServer;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.management.MBeanContainer;
import org.mortbay.util.Scanner;
import com.jfinal.util.PathUtil;
/**
* JettyServer is used to config and start jetty web server.
* Jetty version 6.1.26
*/
/*
* 1: project dir (no use)
* 2: port
* 3: context
* 4: webapp dir
* 5: scan interval senconds
*/
class JettyServer implements IServer {
private String webAppDir;
private int port;
private String context;
private int scanIntervalSeconds;
private boolean isStarted = false;
private Server server;
private WebAppContext web;
private boolean enablescanner = true;
JettyServer(String webAppDir, int port, String context, int scanIntervalSeconds) {
this.webAppDir = webAppDir;
this.port = port;
this.context = context;
this.scanIntervalSeconds = scanIntervalSeconds;
checkConfig();
}
private void checkConfig() {
if (port < 0 || port > 65536)
throw new IllegalArgumentException("Invalid port of web server: " + port);
if (scanIntervalSeconds < 1)
enablescanner = false;
if (context == null)
throw new IllegalStateException("Invalid context of web server: " + context);
if (webAppDir == null)
throw new IllegalStateException("Invalid context of web server: " + webAppDir);
}
public void start() {
if (! isStarted) {
try {
doStart();
} catch (Exception e) {
e.printStackTrace();
}
isStarted = true;
}
else {
throw new RuntimeException("Server already started.");
}
}
private void doStart() throws Exception {
String context = this.context;
String webAppDir = this.webAppDir;
Integer port = this.port;
Integer scanIntervalSeconds = this.scanIntervalSeconds;
server = new Server();
if (port != null) {
if (!available(port)) {
throw new IllegalStateException("port: " + port + " already in use!");
}
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(port);
server.addConnector(connector);
}
web = new WebAppContext();
// 警告: 设置成 true 无法支持热加载
// web.setParentLoaderPriority(false);
web.setContextPath(context);
web.setWar(webAppDir);
web.setInitParams(Collections.singletonMap("org.mortbay.jetty.servlet.Default.useFileMappedBuffer", "false"));
server.addHandler(web);
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
MBeanContainer mBeanContainer = new MBeanContainer(mBeanServer);
server.getContainer().addEventListener(mBeanContainer);
mBeanContainer.start();
// configureScanner
if (enablescanner) {
final ArrayList<File> scanList = new ArrayList<File>();
scanList.add(new File(PathUtil.getRootClassPath()));
Scanner scanner = new Scanner();
scanner.setReportExistingFilesOnStartup(false);
scanner.setScanInterval(scanIntervalSeconds);
scanner.setScanDirs(scanList);
scanner.addListener(new Scanner.BulkListener() {
public void filesChanged(@SuppressWarnings("rawtypes") List changes) {
try {
System.err.println("Loading changes ......");
web.stop();
web.start();
System.err.println("Loading complete.\n");
} catch (Exception e) {
System.err.println("Error reconfiguring/restarting webapp after change in watched files");
e.printStackTrace();
}
}
});
System.err.println("Starting scanner at interval of " + scanIntervalSeconds + " seconds.");
scanner.start();
}
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(100);
}
return;
}
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;
}
}