在项目开发中使用嵌入式tomcat开发,在工程项目中经常用到,其好处我不再赘述,大家可以通过网上的资料对嵌入式tomcat开发有个大致的了解。其技术要点主要是利用Tomcat5中的Embeded版本,在应用中完成集成web服务,项目可以根据情况需要对嵌入tomcat进行相应的扩展。
下载tomcat的Embeded版本,如:Tomcat 5.0.28 Embed tar.gz。在解压 Tomcat Embed 版本后,将其 lib 目录下所有 .jar 文件加入到 Java 项目的 classpath 中,就完成了基本的准备工作了。以下是我在调试嵌入式tomcat时候使用的核心源码,提供出来给大家参考。
package com.lvhs.core.webserver;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Embedded;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.coyote.tomcat5.CoyoteConnector;
/**
* Tomcat嵌入式Server,可以创建一个不支持加密的,侦听在本机127.0.0.1上的一个tomcat服务器
*
* 修改一些默认属性,把一些目录结构和tomcat的server.xml对应起来. 加入webapp应用自动获取解压配置等功能
*
* @author Dennis Lv (weer99@163.com) Create By 2008-7-6 下午05:24:43
*/
public class EmbeddedTomcatServer {
private static final Log logger = LogFactory
.getLog(EmbeddedTomcatServer.class);
public static final int LOG_FATAL = org.apache.catalina.Logger.FATAL;
public static final int LOG_ERROR = org.apache.catalina.Logger.ERROR;
public static final int LOG_WARNING = org.apache.catalina.Logger.WARNING;
public static final int LOG_INFORMATION = org.apache.catalina.Logger.INFORMATION;
public static final int LOG_DEBUG = org.apache.catalina.Logger.DEBUG;
/**
* tomcat日志级别 <code>logLever</code>
*/
private int logLever = LOG_DEBUG;
/**
* tomcat启动端口 <code>port</code>
*/
private int port = 8898;
/**
* 设置tomcat的主目录 <code>catalinaHome</code>
*/
private String catalinaHome = System.getProperty("user.dir") + "/";
/**
* tomcat的engineName <code>engineName</code>
*/
private String engineName = "emeddedTomcatServer";
/**
* 设置应用APP路径,对应通常的tomcat/webapps,appbase必须要球在catalinaHome下面,前面不需要/
* <code>appBase</code>
*/
private String appBase = "webapps/";
/**
* 设置webapp的对应web路径,如果是根路径则为空字符串 <code>docPath</code>
*/
private String docPath = "";
/**
* 设置webapp的docbase,这里docbase要求必须在appbase下,前面没有/ <code>docBase</code>
*/
private String docBase = "ROOT";
private boolean started = false;
private Embedded tomcat = new Embedded();
public boolean isStarted() {
return started;
}
public void startup() throws UnknownHostException, LifecycleException {
if (!started) {
init();
tomcat.start();
started = true;
}
}
public void shutdown() throws LifecycleException {
if (started) {
tomcat.stop();
started = false;
}
}
private void init() throws UnknownHostException {
tomcat.setCatalinaHome(catalinaHome);
Engine engine = tomcat.createEngine();
engine.setName(engineName);
Host host = tomcat.createHost("localhost", tomcat.getCatalinaHome()
+ appBase);
host.setAutoDeploy(true);
if (logger.isInfoEnabled()) {
logger.info("WebApp的根路径为" + appBase);
}
File baseDir = new File(tomcat.getCatalinaHome() + appBase);
// 自动获取webapps下的war,就行解压和自动获取创建Context
if (baseDir.exists()) {
chkAndExtractWar(baseDir);
File lstFile = new File(baseDir, "webapps.lst");
// deploy文件jsp
if (lstFile.exists()) {
lstDeploy(host, baseDir);
} else {
defaultDeploy(host, baseDir);
}
}
engine.setDefaultHost(host.getName());
engine.addChild(host);
tomcat.addEngine(engine);
tomcat.setDebug(logLever);
InetAddress ias = InetAddress.getByName("0.0.0.0");// 可以采用限制本机访问的127.0.0.1
Connector connector = tomcat.createConnector(ias, port, false);
if (connector instanceof CoyoteConnector) {
CoyoteConnector cconnector = (CoyoteConnector) connector;
cconnector.setURIEncoding("GBK");
}
tomcat.addConnector(connector);
if (logger.isInfoEnabled()) {
logger.info("创建连接器" + ias.getHostAddress() + ":" + port);
}
}
/**
* 检查并解压war文件
*
* @param baseDir
*/
private void chkAndExtractWar(File baseDir) {
FileFilter warFF = new FileFilter() {
public boolean accept(File pathname) {
return pathname.getName().endsWith(".war");
}
};
// 获取所有的war,判断是否解压,如果没有则进行解压
File[] warFiles = baseDir.listFiles(warFF);
for (int i = 0; i < warFiles.length; i++) {
String warFN = warFiles[i].getName();
File warDir = new File(baseDir, warFN.substring(0,
warFN.length() - 4));
if (!warDir.exists()) {
try {
extractWar(warFiles[i], warDir);
} catch (Exception e) {
logger.error("解压War文件" + warFN + "到" + warDir + "失败.", e);
}
}
}
}
/**
* 根据war文件,以及解压到目的目录文件,对war文件进行解压
*
* @param warFile
* @param targetDir
* @throws Exception
*/
private static void extractWar(File warFile, File targetDir)
throws Exception {
ZipFile zfile = null;
try {
zfile = new ZipFile(warFile);
Enumeration zlist = zfile.entries();
ZipEntry ze = null;
byte[] buf = new byte[1024];
while (zlist.hasMoreElements()) {
// 从ZipFile中得到一个ZipEntry
ze = (ZipEntry) zlist.nextElement();
String zeName = ze.getName();
if (ze.isDirectory()) {
createDir(targetDir, zeName);
continue;
}
// 写入文件
OutputStream os = null;
InputStream is = null;
try {
os = new BufferedOutputStream(new FileOutputStream(
getRealFile(targetDir, zeName)));
is = new BufferedInputStream(zfile.getInputStream(ze));
int len = 0;
while ((len = is.read(buf, 0, buf.length)) != -1) {
os.write(buf, 0, len);
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
}
} finally {
if (zfile != null) {
try {
zfile.close();
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
}
}
}
/**
* 根据根目录,以及目录路径名称,创建目录结构
*
* @param baseDir
* @param dirName
* @return
*/
private static File createDir(File baseDir, String dirName) {
String[] dirs = dirName.split("/");
File ret = baseDir;
if (dirs.length > 1) {
for (int i = 0; i < dirs.length; i++) {
ret = new File(ret, dirs[i]);
}
}
if (!ret.exists()) {
ret.mkdirs();
}
return ret;
}
/**
* 根据相对目录路径,以及根文件目录,创建实际的文件jsp
*
* @param baseDir
* @param absFileName
* @return
*/
private static File getRealFile(File baseDir, String absFileName) {
String[] dirs = absFileName.split("/");
File ret = baseDir;
if (dirs.length > 1) {
for (int i = 0; i < dirs.length - 1; i++) {
ret = new File(ret, dirs[i]);
}
}
if (!ret.exists()) {
ret.mkdirs();
}
ret = new File(ret, dirs[dirs.length - 1]);
return ret;
}
/**
* 默认装载方式
*
* @param host
* @param baseDir
*/
private void defaultDeploy(Host host, File baseDir) {
FileFilter appDirFF = new FileFilter() {
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
// 目录下必须有WEB-INF和WEB-INF目录下的web.xml
File webInf = new File(pathname, "WEB-INF");
return webInf.exists()
&& new File(webInf, "web.xml").exists();
}
return false;
}
};
// 获取所有目录,并转成Context在内嵌tomcat中加载
File[] appDirs = baseDir.listFiles(appDirFF);
for (int i = 0; i < appDirs.length; i++) {
String appDirName = appDirs[i].getName();
String appPath = "/" + appDirName;
if ("ROOT".equalsIgnoreCase(appDirName)) {
appPath = "";
}
Context ctxRoot = tomcat.createContext(appPath, appDirName);
ctxRoot.setPrivileged(true);
ctxRoot.setReloadable(true);
ctxRoot.addParameter("debug", "0");
host.addChild(ctxRoot);
if (logger.isInfoEnabled()) {
logger.info("appPath:" + appPath);
logger.info("装载了Web应用" + appDirName);
}
}
}
/**
* 按指定列表的方式加载
*
* @param host
* @param baseDir
*/
private void lstDeploy(Host host, File baseDir) {
File lstFile = new File(baseDir, "webapps.lst");
if (!lstFile.exists()) {
logger.info("使用了指定方式加载Web模块,但" + lstFile.getPath() + "不存在.");
return;
}
FileInputStream fis = null;
try {
Properties prop = new Properties();
fis = new FileInputStream(lstFile);
prop.load(fis);
Set entrySet = prop.entrySet();
for (Iterator iter = entrySet.iterator(); iter.hasNext();) {
Entry e = (Entry) iter.next();
String key = (String) e.getKey();
String val = (String) e.getValue();
if (val == null || val.trim().length() <= 0) {
if (logger.isDebugEnabled()) {
logger.info("应用[" + key + "]所指定的文件路径为空.");
}
continue;
} else {
Context ctxRoot = tomcat.createContext(key, val);
ctxRoot.setPrivileged(true);
ctxRoot.setReloadable(true);
ctxRoot.addParameter("debug", "0");
host.addChild(ctxRoot);
if (logger.isInfoEnabled()) {
logger.info("装载了Web应用" + key + "(" + val + ")");
}
}
}
} catch (FileNotFoundException e) {
logger.error(lstFile.getPath() + "不存在.", e);
} catch (IOException e) {
logger.error("加载" + lstFile.getPath() + "属性文件出错.", e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
}
}
}
}
public String getAppBase() {
return appBase;
}
public void setAppBase(String appBase) {
this.appBase = appBase;
}
public String getDocBase() {
return docBase;
}
public void setDocBase(String docBase) {
this.docBase = docBase;
}
public String getDocPath() {
return docPath;
}
public void setDocPath(String docPath) {
this.docPath = docPath;
}
public String getEngineName() {
return engineName;
}
public void setEngineName(String engineName) {
this.engineName = engineName;
}
public String getCatalinaHome() {
return catalinaHome;
}
public void setCatalinaHome(String catalinaHome) {
this.catalinaHome = catalinaHome;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getLogLever() {
return logLever;
}
public void setLogLever(int logLever) {
this.logLever = logLever;
}
}
可以在适当的时候调用startup()和shutdown()方法启动和停止web服务。
开发时候需要注意的一些问题:
- 运行此程序时需要使用 JDK 而非 JRE,因为 Tomcat 需要动态编译 JSP 页面,可能还需要手工把 JDK 的 /lib/tools.jar 加入到项目 classpath 中。
- 因为嵌入式版本 Tomcat 没有 common/lib 目录,如果碰到 JAXP 的 Provider 没有找到的 bug,可能需要直接将 xercesImpl.jar 等实现包加入到项目 classpath 中。
- 注意 classpath 中不要有其他版本 tomcat 的包,否则可能会出现冲突。
- 启动web服务的时候需要加载一些配置文件,在工程项目下需要有conf/web.xml配置文件,直接从tomcat拷贝也行,也可以进行相应的配置对web服务进行扩展。
Over,呵呵,庆祝开博小发文章以供大家项目应用中参考。