mirror of
https://gitee.com/nutz/nutzboot.git
synced 2024-12-04 20:58:22 +08:00
commit
a149ca8662
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>nutzboot-demo-simple</artifactId>
|
||||
<groupId>org.nutz</groupId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>nutzboot-demo-simple-tomcat</artifactId>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer />
|
||||
<transformer>
|
||||
<resource>META-INF/nutz/org.nutz.boot.starter.NbStarter</resource>
|
||||
</transformer>
|
||||
<transformer>
|
||||
<mainClass>io.nutz.demo.simple.MainLauncher</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@ -0,0 +1,74 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutzboot-demo-simple</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nutzboot-demo-simple-tomcat</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutzboot-starter-nutz-mvc</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutzboot-starter-tomcat</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutzboot-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jstl</groupId>
|
||||
<artifactId>jstl</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>taglibs</groupId>
|
||||
<artifactId>standard</artifactId>
|
||||
<version>1.1.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/nutz/org.nutz.boot.starter.NbStarter</resource>
|
||||
</transformer>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>io.nutz.demo.simple.MainLauncher</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,21 @@
|
||||
package io.nutz.demo.simple;
|
||||
|
||||
import org.nutz.boot.NbApp;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
@IocBean
|
||||
public class MainLauncher {
|
||||
|
||||
@Ok("raw")
|
||||
@At("/time/now")
|
||||
public long now() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new NbApp(MainLauncher.class).setPrintProcDoc(true).run();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
tomcat.port=8080
|
||||
tomcat.host=0.0.0.0
|
@ -0,0 +1,7 @@
|
||||
log4j.rootLogger=debug,Console
|
||||
|
||||
log4j.logger.org.eclipse.jetty=info
|
||||
|
||||
log4j.appender.Console=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.Console.layout.ConversionPattern=[%-5p] %d{HH:mm:ss.SSS} %l - %m%n
|
@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello, So NB!</title>
|
||||
</head>
|
||||
<body>
|
||||
Hello, So NB!(Tomcat)
|
||||
</body>
|
||||
</html>
|
@ -19,6 +19,7 @@
|
||||
<module>nutzboot-demo-simple-mvc-shiro</module>
|
||||
<module>nutzboot-demo-simple-swagger</module>
|
||||
<module>nutzboot-demo-simple-undertow</module>
|
||||
<module>nutzboot-demo-simple-tomcat</module>
|
||||
<module>nutzboot-demo-simple-thymeleaf</module>
|
||||
<module>nutzboot-demo-simple-sharding-jdbc</module>
|
||||
<module>nutzboot-demo-simple-uflo</module>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nutzboot-starter-tomcat</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<name>nutzboot-starter-tomcat</name>
|
||||
<properties>
|
||||
@ -32,6 +33,12 @@
|
||||
<email>wendal1985@gmail.com</email>
|
||||
<url>http://wendal.net/</url>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>wolfboys</id>
|
||||
<name>benjobs</name>
|
||||
<email>benjobs@qq.com</email>
|
||||
<url>https://github.com/wolfboys</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/nutzam/nutzboot.git</connection>
|
||||
@ -51,4 +58,70 @@
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutzboot-starter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--tomcat start-->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-el</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-jasper</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-logging-juli</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jdt.core.compiler</groupId>
|
||||
<artifactId>ecj</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--tomcat end-->
|
||||
<dependency>
|
||||
<groupId>org.nutz</groupId>
|
||||
<artifactId>nutz-plugins-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-core</artifactId>
|
||||
<version>RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>5.0.1.RELEASE</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
<version>1.5.6.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,204 @@
|
||||
package org.nutz.boot.starter.tomcat;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.nutz.lang.Files;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.net.URLConnection;
|
||||
import java.security.CodeSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
|
||||
public abstract class AbstractServletContainerFactory {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private static final String[] COMMON_DOC_ROOTS = { "webapp", "public","static" };
|
||||
|
||||
private File documentRoot;
|
||||
|
||||
public AbstractServletContainerFactory() {
|
||||
super();
|
||||
}
|
||||
|
||||
public File getDocumentRoot() {
|
||||
return this.documentRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute document root when it points to a valid directory, logging a
|
||||
* warning and returning {@code null} otherwise.
|
||||
* @return the valid document root
|
||||
*/
|
||||
protected final File getValidDocumentRoot(String staticPath) {
|
||||
File file = getDocumentRoot();
|
||||
// If document root not explicitly set see if we are running from a war archive
|
||||
file = file != null ? file : getWarFileDocumentRoot();
|
||||
// If not a war archive maybe it is an exploded war
|
||||
file = file != null ? file : getExplodedWarFileDocumentRoot();
|
||||
// Or maybe there is a document root in a well-known location
|
||||
file = file != null ? file : getCommonDocumentRoot();
|
||||
//static file
|
||||
file = file != null ? file : Files.findFile(staticPath);
|
||||
if (file == null && this.logger.isDebugEnabled()) {
|
||||
this.logger
|
||||
.debug("None of the document roots " + Arrays.asList(COMMON_DOC_ROOTS)
|
||||
+ " point to a directory and will be ignored.");
|
||||
}
|
||||
else if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Document root: " + file);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private File getExplodedWarFileDocumentRoot() {
|
||||
return getExplodedWarFileDocumentRoot(getCodeSourceArchive());
|
||||
}
|
||||
|
||||
protected List<URL> getUrlsOfJarsWithMetaInfResources() {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
List<URL> staticResourceUrls = new ArrayList<URL>();
|
||||
if (classLoader instanceof URLClassLoader) {
|
||||
for (URL url : ((URLClassLoader) classLoader).getURLs()) {
|
||||
try {
|
||||
if ("file".equals(url.getProtocol())) {
|
||||
File file = new File(url.getFile());
|
||||
if (file.isDirectory()
|
||||
&& new File(file, "META-INF/resources").isDirectory()) {
|
||||
staticResourceUrls.add(url);
|
||||
}
|
||||
else if (isResourcesJar(file)) {
|
||||
staticResourceUrls.add(url);
|
||||
}
|
||||
}
|
||||
else {
|
||||
URLConnection connection = url.openConnection();
|
||||
if (connection instanceof JarURLConnection) {
|
||||
if (isResourcesJar((JarURLConnection) connection)) {
|
||||
staticResourceUrls.add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return staticResourceUrls;
|
||||
}
|
||||
|
||||
private boolean isResourcesJar(JarURLConnection connection) {
|
||||
try {
|
||||
return isResourcesJar(connection.getJarFile());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isResourcesJar(File file) {
|
||||
try {
|
||||
return isResourcesJar(new JarFile(file));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isResourcesJar(JarFile jar) throws IOException {
|
||||
try {
|
||||
return jar.getName().endsWith(".jar")
|
||||
&& (jar.getJarEntry("META-INF/resources") != null);
|
||||
}
|
||||
finally {
|
||||
jar.close();
|
||||
}
|
||||
}
|
||||
|
||||
File getExplodedWarFileDocumentRoot(File codeSourceFile) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Code archive: " + codeSourceFile);
|
||||
}
|
||||
if (codeSourceFile != null && codeSourceFile.exists()) {
|
||||
String path = codeSourceFile.getAbsolutePath();
|
||||
int webInfPathIndex = path
|
||||
.indexOf(File.separatorChar + "WEB-INF" + File.separatorChar);
|
||||
if (webInfPathIndex >= 0) {
|
||||
path = path.substring(0, webInfPathIndex);
|
||||
return new File(path);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File getWarFileDocumentRoot() {
|
||||
return getArchiveFileDocumentRoot(".war");
|
||||
}
|
||||
|
||||
private File getArchiveFileDocumentRoot(String extension) {
|
||||
File file = getCodeSourceArchive();
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Code archive: " + file);
|
||||
}
|
||||
if (file != null && file.exists() && !file.isDirectory()
|
||||
&& file.getName().toLowerCase().endsWith(extension)) {
|
||||
return file.getAbsoluteFile();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File getCommonDocumentRoot() {
|
||||
for (String commonDocRoot : COMMON_DOC_ROOTS) {
|
||||
URL url = Thread.currentThread().getContextClassLoader().getResource(commonDocRoot);
|
||||
if (url!=null) {
|
||||
File root = new File(url.getPath());
|
||||
if (root.exists() && root.isDirectory()) {
|
||||
return root.getAbsoluteFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File getCodeSourceArchive() {
|
||||
return getCodeSourceArchive(getClass().getProtectionDomain().getCodeSource());
|
||||
}
|
||||
|
||||
File getCodeSourceArchive(CodeSource codeSource) {
|
||||
try {
|
||||
URL location = (codeSource == null ? null : codeSource.getLocation());
|
||||
if (location == null) {
|
||||
return null;
|
||||
}
|
||||
String path;
|
||||
URLConnection connection = location.openConnection();
|
||||
if (connection instanceof JarURLConnection) {
|
||||
path = ((JarURLConnection) connection).getJarFile().getName();
|
||||
}
|
||||
else {
|
||||
path = location.toURI().getPath();
|
||||
}
|
||||
if (path.contains("!/")) {
|
||||
path = path.substring(0, path.indexOf("!/"));
|
||||
}
|
||||
return new File(path);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected final File getValidSessionStoreDir(boolean mkdirs) {
|
||||
return new ApplicationTemp().getDir("servlet-sessions");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
package org.nutz.boot.starter.tomcat;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class ApplicationHome {
|
||||
|
||||
private final File source;
|
||||
|
||||
private final File dir;
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationHome} instance.
|
||||
*/
|
||||
public ApplicationHome() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationHome} instance for the specified source class.
|
||||
* @param sourceClass the source class or {@code null}
|
||||
*/
|
||||
public ApplicationHome(Class<?> sourceClass) {
|
||||
this.source = findSource(sourceClass == null ? getStartClass() : sourceClass);
|
||||
this.dir = findHomeDir(this.source);
|
||||
}
|
||||
|
||||
private Class<?> getStartClass() {
|
||||
try {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
return getStartClass(classLoader.getResources("META-INF/MANIFEST.MF"));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> getStartClass(Enumeration<URL> manifestResources) {
|
||||
while (manifestResources.hasMoreElements()) {
|
||||
try {
|
||||
InputStream inputStream = manifestResources.nextElement().openStream();
|
||||
try {
|
||||
Manifest manifest = new Manifest(inputStream);
|
||||
String startClass = manifest.getMainAttributes()
|
||||
.getValue("Start-Class");
|
||||
if (startClass != null) {
|
||||
return ClassUtils.forName(startClass,
|
||||
getClass().getClassLoader());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File findSource(Class<?> sourceClass) {
|
||||
try {
|
||||
ProtectionDomain domain = (sourceClass == null ? null
|
||||
: sourceClass.getProtectionDomain());
|
||||
CodeSource codeSource = (domain == null ? null : domain.getCodeSource());
|
||||
URL location = (codeSource == null ? null : codeSource.getLocation());
|
||||
File source = (location == null ? null : findSource(location));
|
||||
if (source != null && source.exists() && !isUnitTest()) {
|
||||
return source.getAbsoluteFile();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUnitTest() {
|
||||
try {
|
||||
for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
|
||||
if (element.getClassName().startsWith("org.junit.")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private File findSource(URL location) throws IOException {
|
||||
URLConnection connection = location.openConnection();
|
||||
if (connection instanceof JarURLConnection) {
|
||||
return getRootJarFile(((JarURLConnection) connection).getJarFile());
|
||||
}
|
||||
return new File(location.getPath());
|
||||
}
|
||||
|
||||
private File getRootJarFile(JarFile jarFile) {
|
||||
String name = jarFile.getName();
|
||||
int separator = name.indexOf("!/");
|
||||
if (separator > 0) {
|
||||
name = name.substring(0, separator);
|
||||
}
|
||||
return new File(name);
|
||||
}
|
||||
|
||||
private File findHomeDir(File source) {
|
||||
File homeDir = source;
|
||||
homeDir = (homeDir == null ? findDefaultHomeDir() : homeDir);
|
||||
if (homeDir.isFile()) {
|
||||
homeDir = homeDir.getParentFile();
|
||||
}
|
||||
homeDir = (homeDir.exists() ? homeDir : new File("."));
|
||||
return homeDir.getAbsoluteFile();
|
||||
}
|
||||
|
||||
private File findDefaultHomeDir() {
|
||||
String userDir = System.getProperty("user.dir");
|
||||
return new File(StringUtils.hasLength(userDir) ? userDir : ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying source used to find the home directory. This is usually the
|
||||
* jar file or a directory. Can return {@code null} if the source cannot be
|
||||
* determined.
|
||||
* @return the underlying source or {@code null}
|
||||
*/
|
||||
public File getSource() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application home directory.
|
||||
* @return the home directory (never {@code null})
|
||||
*/
|
||||
public File getDir() {
|
||||
return this.dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getDir().toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package org.nutz.boot.starter.tomcat;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class ApplicationTemp {
|
||||
|
||||
private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
private final Class<?> sourceClass;
|
||||
|
||||
private volatile File dir;
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationTemp} instance.
|
||||
*/
|
||||
public ApplicationTemp() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ApplicationTemp} instance for the specified source class.
|
||||
* @param sourceClass the source class or {@code null}
|
||||
*/
|
||||
public ApplicationTemp(Class<?> sourceClass) {
|
||||
this.sourceClass = sourceClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getDir().getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a sub-directory of the application temp.
|
||||
* @param subDir the sub-directory name
|
||||
* @return a sub-directory
|
||||
*/
|
||||
public File getDir(String subDir) {
|
||||
File dir = new File(getDir(), subDir);
|
||||
dir.mkdirs();
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the directory to be used for application specific temp files.
|
||||
* @return the application temp directory
|
||||
*/
|
||||
public File getDir() {
|
||||
if (this.dir == null) {
|
||||
synchronized (this) {
|
||||
byte[] hash = generateHash(this.sourceClass);
|
||||
this.dir = new File(getTempDirectory(), toHexString(hash));
|
||||
this.dir.mkdirs();
|
||||
Assert.state(this.dir.exists(),
|
||||
"Unable to create temp directory " + this.dir);
|
||||
}
|
||||
}
|
||||
return this.dir;
|
||||
}
|
||||
|
||||
private File getTempDirectory() {
|
||||
String property = System.getProperty("java.io.tmpdir");
|
||||
Assert.state(StringUtils.hasLength(property), "No 'java.io.tmpdir' property set");
|
||||
File file = new File(property);
|
||||
Assert.state(file.exists(), "Temp directory" + file + " does not exist");
|
||||
Assert.state(file.isDirectory(), "Temp location " + file + " is not a directory");
|
||||
return file;
|
||||
}
|
||||
|
||||
private byte[] generateHash(Class<?> sourceClass) {
|
||||
ApplicationHome home = new ApplicationHome(sourceClass);
|
||||
MessageDigest digest;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA-1");
|
||||
update(digest, home.getSource());
|
||||
update(digest, home.getDir());
|
||||
update(digest, System.getProperty("user.dir"));
|
||||
update(digest, System.getProperty("java.home"));
|
||||
update(digest, System.getProperty("java.class.path"));
|
||||
update(digest, System.getProperty("sun.java.command"));
|
||||
update(digest, System.getProperty("sun.boot.class.path"));
|
||||
return digest.digest();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void update(MessageDigest digest, Object source) {
|
||||
if (source != null) {
|
||||
digest.update(getUpdateSourceBytes(source));
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getUpdateSourceBytes(Object source) {
|
||||
if (source instanceof File) {
|
||||
return getUpdateSourceBytes(((File) source).getAbsolutePath());
|
||||
}
|
||||
return source.toString().getBytes();
|
||||
}
|
||||
|
||||
private String toHexString(byte[] bytes) {
|
||||
char[] hex = new char[bytes.length * 2];
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
int b = bytes[i] & 0xFF;
|
||||
hex[i * 2] = HEX_CHARS[b >>> 4];
|
||||
hex[i * 2 + 1] = HEX_CHARS[b & 0x0F];
|
||||
}
|
||||
return new String(hex);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.nutz.boot.starter.tomcat;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.LifecycleState;
|
||||
import org.apache.catalina.util.StandardSessionIdGenerator;
|
||||
|
||||
|
||||
class LazySessionIdGenerator extends StandardSessionIdGenerator {
|
||||
|
||||
@Override
|
||||
protected void startInternal() throws LifecycleException {
|
||||
setState(LifecycleState.STARTING);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package org.nutz.boot.starter.tomcat;
|
||||
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Manager;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.session.ManagerBase;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
public class TomcatContext extends StandardContext {
|
||||
|
||||
private TomcatStarter starter;
|
||||
|
||||
private final boolean overrideLoadOnStart;
|
||||
|
||||
TomcatContext() {
|
||||
this.overrideLoadOnStart = ReflectionUtils
|
||||
.findMethod(StandardContext.class, "loadOnStartup", Container[].class)
|
||||
.getReturnType() == boolean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean loadOnStartup(Container[] children) {
|
||||
if (this.overrideLoadOnStart) {
|
||||
return true;
|
||||
}
|
||||
return super.loadOnStartup(children);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setManager(Manager manager) {
|
||||
if (manager instanceof ManagerBase) {
|
||||
manager.setSessionIdGenerator(new LazySessionIdGenerator());
|
||||
}
|
||||
super.setManager(manager);
|
||||
}
|
||||
|
||||
public void deferredLoadOnStartup() {
|
||||
// Some older Servlet frameworks (e.g. Struts, BIRT) use the Thread context class
|
||||
// loader to create servlet instances in this phase. If they do that and then try
|
||||
// to initialize them later the class loader may have changed, so wrap the call to
|
||||
// loadOnStartup in what we think its going to be the main webapp classloader at
|
||||
// runtime.
|
||||
ClassLoader classLoader = getLoader().getClassLoader();
|
||||
ClassLoader existingLoader = null;
|
||||
if (classLoader != null) {
|
||||
existingLoader = ClassUtils.overrideThreadContextClassLoader(classLoader);
|
||||
}
|
||||
|
||||
if (this.overrideLoadOnStart) {
|
||||
// Earlier versions of Tomcat used a version that returned void. If that
|
||||
// version is used our overridden loadOnStart method won't have been called
|
||||
// and the original will have already run.
|
||||
super.loadOnStartup(findChildren());
|
||||
}
|
||||
if (existingLoader != null) {
|
||||
ClassUtils.overrideThreadContextClassLoader(existingLoader);
|
||||
}
|
||||
}
|
||||
|
||||
public void setStarter(TomcatStarter starter) {
|
||||
this.starter = starter;
|
||||
}
|
||||
|
||||
public TomcatStarter getStarter() {
|
||||
return this.starter;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
package org.nutz.boot.starter.tomcat;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.WebResourceRoot.ResourceSetType;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import javax.naming.directory.DirContext;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public abstract class TomcatResources {
|
||||
|
||||
private final Context context;
|
||||
|
||||
TomcatResources(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
void addResourceJars(List<URL> resourceJarUrls) {
|
||||
for (URL url : resourceJarUrls) {
|
||||
String file = url.getFile();
|
||||
if (file.endsWith(".jar") || file.endsWith(".jar!/")) {
|
||||
String jar = url.toString();
|
||||
if (!jar.startsWith("jar:")) {
|
||||
// A jar file in the file system. Convert to Jar URL.
|
||||
jar = "jar:" + jar + "!/";
|
||||
}
|
||||
addJar(jar);
|
||||
}
|
||||
else {
|
||||
addDir(file, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final Context getContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to add a JAR to the resources.
|
||||
* @param jar the URL spec for the jar
|
||||
*/
|
||||
protected abstract void addJar(String jar);
|
||||
|
||||
/**
|
||||
* Called to add a dir to the resource.
|
||||
* @param dir the dir
|
||||
* @param url the URL
|
||||
*/
|
||||
protected abstract void addDir(String dir, URL url);
|
||||
|
||||
/**
|
||||
* Return a {@link TomcatResources} instance for the currently running Tomcat version.
|
||||
* @param context the tomcat context
|
||||
* @return a {@link TomcatResources} instance.
|
||||
*/
|
||||
public static TomcatResources get(Context context) {
|
||||
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) {
|
||||
return new Tomcat7Resources(context);
|
||||
}
|
||||
return new Tomcat8Resources(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TomcatResources} for Tomcat 7.
|
||||
*/
|
||||
private static class Tomcat7Resources extends TomcatResources {
|
||||
|
||||
private final Method addResourceJarUrlMethod;
|
||||
|
||||
Tomcat7Resources(Context context) {
|
||||
super(context);
|
||||
this.addResourceJarUrlMethod = ReflectionUtils.findMethod(context.getClass(),
|
||||
"addResourceJarUrl", URL.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addJar(String jar) {
|
||||
URL url = getJarUrl(jar);
|
||||
if (url != null) {
|
||||
try {
|
||||
this.addResourceJarUrlMethod.invoke(getContext(), url);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private URL getJarUrl(String jar) {
|
||||
try {
|
||||
return new URL(jar);
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
// Ignore
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDir(String dir, URL url) {
|
||||
if (getContext() instanceof StandardContext) {
|
||||
try {
|
||||
Class<?> fileDirContextClass = Class
|
||||
.forName("org.apache.naming.resources.FileDirContext");
|
||||
Method setDocBaseMethod = ReflectionUtils
|
||||
.findMethod(fileDirContextClass, "setDocBase", String.class);
|
||||
Object fileDirContext = fileDirContextClass.newInstance();
|
||||
setDocBaseMethod.invoke(fileDirContext, dir);
|
||||
Method addResourcesDirContextMethod = ReflectionUtils.findMethod(
|
||||
StandardContext.class, "addResourcesDirContext",
|
||||
DirContext.class);
|
||||
addResourcesDirContextMethod.invoke(getContext(), fileDirContext);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Tomcat 7 reflection failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link TomcatResources} for Tomcat 8.
|
||||
*/
|
||||
static class Tomcat8Resources extends TomcatResources {
|
||||
|
||||
Tomcat8Resources(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addJar(String jar) {
|
||||
addResourceSet(jar);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDir(String dir, URL url) {
|
||||
addResourceSet(url.toString());
|
||||
}
|
||||
|
||||
private void addResourceSet(String resource) {
|
||||
try {
|
||||
if (isInsideNestedJar(resource)) {
|
||||
// It's a nested jar but we now don't want the suffix because Tomcat
|
||||
// is going to try and locate it as a root URL (not the resource
|
||||
// inside it)
|
||||
resource = resource.substring(0, resource.length() - 2);
|
||||
}
|
||||
URL url = new URL(resource);
|
||||
String path = "/META-INF/resources";
|
||||
getContext().getResources().createWebResourceSet(
|
||||
ResourceSetType.RESOURCE_JAR, "/", url, path);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Ignore (probably not a directory)
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInsideNestedJar(String dir) {
|
||||
return dir.indexOf("!/") < dir.lastIndexOf("!/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,583 @@
|
||||
package org.nutz.boot.starter.tomcat;
|
||||
|
||||
|
||||
import org.apache.catalina.*;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.catalina.core.StandardThreadExecutor;
|
||||
import org.apache.catalina.loader.WebappLoader;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.jasper.servlet.JspServlet;
|
||||
import org.apache.naming.ContextBindings;
|
||||
import org.apache.tomcat.util.descriptor.web.FilterDef;
|
||||
import org.nutz.boot.AppContext;
|
||||
import org.nutz.boot.annotation.PropDoc;
|
||||
import org.nutz.boot.aware.AppContextAware;
|
||||
import org.nutz.boot.aware.ClassLoaderAware;
|
||||
import org.nutz.boot.aware.IocAware;
|
||||
import org.nutz.boot.starter.ServerFace;
|
||||
import org.nutz.boot.starter.WebEventListenerFace;
|
||||
import org.nutz.boot.starter.WebFilterFace;
|
||||
import org.nutz.boot.starter.WebServletFace;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.LifeCycle;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
|
||||
import org.springframework.boot.context.embedded.tomcat.ConnectorStartFailedException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.servlet.ServletContext;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* tomcat 启动器
|
||||
*
|
||||
* @author benjobs (benjobs@qq.com)
|
||||
*/
|
||||
public class TomcatStarter extends AbstractServletContainerFactory implements ClassLoaderAware, IocAware, ServerFace, LifeCycle, AppContextAware {
|
||||
|
||||
private static final Log logger = Logs.get();
|
||||
|
||||
protected static final String PRE = "tomcat.";
|
||||
|
||||
@PropDoc(group = "tomcat", value = "监听的ip地址", defaultValue = "0.0.0.0")
|
||||
public static final String PROP_HOST = PRE + "host";
|
||||
|
||||
@PropDoc(group = "tomcat", value = "监听的端口", defaultValue = "8080", type = "int")
|
||||
public static final String PROP_PORT = PRE + "port";
|
||||
|
||||
@PropDoc(group = "tomcat", value = "上下文路径")
|
||||
public static final String PROP_CONTEXT_PATH = PRE + "contextPath";
|
||||
|
||||
@PropDoc(group = "tomcat", value = "session过期时间", defaultValue = "20")
|
||||
public static final String PROP_SESSION = PRE + "session";
|
||||
|
||||
@PropDoc(group = "tomcat", value = "过滤器顺序", defaultValue = "whale,druid,shiro,nutz")
|
||||
public static final String PROP_WEB_FILTERS_ORDER = "web.filters.order";
|
||||
|
||||
@PropDoc(group = "tomcat", value = "静态文件路径", defaultValue = "/static/")
|
||||
public static final String PROP_STATIC_PATH = PRE + "staticPath";
|
||||
|
||||
public static final String PROP_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
|
||||
|
||||
protected Tomcat tomcat;
|
||||
protected TomcatContext tomcatContext;
|
||||
|
||||
protected ClassLoader classLoader;
|
||||
protected Ioc ioc;
|
||||
protected AppContext appContext;
|
||||
private PropertiesProxy conf;
|
||||
|
||||
|
||||
private final Map<Service, Connector[]> serviceConnectors = new HashMap<Service, Connector[]>();
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private static final AtomicInteger containerCounter = new AtomicInteger(-1);
|
||||
|
||||
private Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
|
||||
|
||||
private Charset uriEncoding = DEFAULT_CHARSET;
|
||||
|
||||
private final boolean autoStart = true;
|
||||
|
||||
private volatile boolean started;
|
||||
|
||||
//--getConf---
|
||||
public int getPort() {
|
||||
return conf.getInt(PROP_PORT, 8080);
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return conf.get(PROP_HOST, "0.0.0.0");
|
||||
}
|
||||
|
||||
public String getStaticPath() {
|
||||
return conf.get(PROP_STATIC_PATH, "static");
|
||||
}
|
||||
|
||||
public String getContextPath() {
|
||||
return conf.get(PROP_CONTEXT_PATH, "");
|
||||
}
|
||||
|
||||
public int getSessionTimeout() {
|
||||
return conf.getInt(PROP_SESSION, 30);
|
||||
}
|
||||
|
||||
public int getMaxThread() {
|
||||
return Lang.isAndroid ? 50 : 500;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.conf = appContext.getConfigureLoader().get();
|
||||
|
||||
this.tomcat = new Tomcat();
|
||||
|
||||
File baseDir = createTempDir("tomcat");
|
||||
this.tomcat.setBaseDir(baseDir.getAbsolutePath());
|
||||
|
||||
Connector connector = new Connector(this.PROP_PROTOCOL);
|
||||
connector.setPort(getPort());
|
||||
connector.setURIEncoding(getUriEncoding().name());
|
||||
|
||||
//maxThread
|
||||
this.tomcat.getService().addConnector(connector);
|
||||
StandardThreadExecutor executor = new StandardThreadExecutor();
|
||||
executor.setMaxThreads(getMaxThread());
|
||||
connector.getService().addExecutor(executor);
|
||||
|
||||
// If ApplicationContext is slow to start we want Tomcat not to bind to the socket
|
||||
// prematurely...
|
||||
connector.setProperty("bindOnInit", "false");
|
||||
|
||||
this.tomcat.setConnector(connector);
|
||||
|
||||
this.tomcat.setHostname(getHost());
|
||||
this.tomcat.getHost().setAutoDeploy(false);
|
||||
this.tomcat.getEngine().setBackgroundProcessorDelay(30);
|
||||
|
||||
prepareContext(this.tomcat.getHost());
|
||||
|
||||
try {
|
||||
|
||||
addInstanceIdToEngineName();
|
||||
|
||||
removeServiceConnectors();
|
||||
|
||||
this.tomcat.start();
|
||||
|
||||
this.nutzSupport();
|
||||
|
||||
rethrowDeferredStartupExceptions();
|
||||
|
||||
Context context = findContext();
|
||||
try {
|
||||
ContextBindings.bindClassLoader(
|
||||
context,
|
||||
getNamingToken(context),
|
||||
getClass().getClassLoader()
|
||||
);
|
||||
} catch (NamingException ex) {
|
||||
}
|
||||
startDaemonAwaitThread();
|
||||
} catch (Exception ex) {
|
||||
this.containerCounter.decrementAndGet();
|
||||
throw new EmbeddedServletContainerException("Unable to start embedded Tomcat", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws EmbeddedServletContainerException {
|
||||
synchronized (this.monitor) {
|
||||
if (this.started) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
addPreviouslyRemovedConnectors();
|
||||
Connector connector = this.tomcat.getConnector();
|
||||
if (connector != null && this.autoStart) {
|
||||
startConnector(connector);
|
||||
}
|
||||
checkThatConnectorsHaveStarted();
|
||||
this.started = true;
|
||||
logger.info("Tomcat started on port(s): " + getPortsDescription(true));
|
||||
} catch (ConnectorStartFailedException ex) {
|
||||
stopSilently();
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new EmbeddedServletContainerException(
|
||||
"Unable to start embedded Tomcat servlet container", ex);
|
||||
} finally {
|
||||
Context context = findContext();
|
||||
ContextBindings.unbindClassLoader(context, getNamingToken(context),
|
||||
getClass().getClassLoader());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws LifecycleException {
|
||||
this.tomcat.stop();
|
||||
this.started = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.started;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAppContext(AppContext appContext) {
|
||||
this.appContext = appContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClassLoader(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIoc(Ioc ioc) {
|
||||
this.ioc = ioc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean failsafe() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void fetch() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void depose() {
|
||||
|
||||
}
|
||||
|
||||
private void prepareContext(Host host) {
|
||||
|
||||
File docBase = getValidDocumentRoot(getStaticPath());
|
||||
docBase = (docBase != null ? docBase : createTempDir("tomcat-docbase"));
|
||||
|
||||
this.tomcatContext = new TomcatContext();
|
||||
this.tomcatContext.setName(getContextPath());
|
||||
this.tomcatContext.setPath(getContextPath());
|
||||
this.tomcatContext.setDocBase(docBase.getAbsolutePath());
|
||||
this.tomcatContext.addLifecycleListener(new Tomcat.FixContextListener());
|
||||
this.tomcatContext.setParentClassLoader(ClassUtils.getDefaultClassLoader());
|
||||
|
||||
for (String welcomeFile : Arrays.asList("index.html", "index.htm", "index.jsp", "index.do")) {
|
||||
this.tomcatContext.addWelcomeFile(welcomeFile);
|
||||
}
|
||||
|
||||
resetDefaultLocaleMapping();
|
||||
|
||||
try {
|
||||
this.tomcatContext.setUseRelativeRedirects(false);
|
||||
} catch (NoSuchMethodError ex) {
|
||||
// Tomcat is < 8.0.30. Continue
|
||||
}
|
||||
WebappLoader loader = new WebappLoader(tomcatContext.getParentClassLoader());
|
||||
loader.setLoaderClass(TomcatWebappClassLoader.class.getName());
|
||||
loader.setDelegate(true);
|
||||
this.tomcatContext.setLoader(loader);
|
||||
|
||||
//注册defaultServlet
|
||||
addDefaultServlet();
|
||||
|
||||
//注册JspServlet
|
||||
addJspServlet();
|
||||
|
||||
this.tomcatContext.addLifecycleListener(new StoreMergedWebXmlListener());
|
||||
|
||||
this.tomcatContext.addLifecycleListener(new LifecycleListener() {
|
||||
@Override
|
||||
public void lifecycleEvent(LifecycleEvent event) {
|
||||
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
|
||||
TomcatResources.get(TomcatStarter.this.tomcatContext)
|
||||
.addResourceJars(getUrlsOfJarsWithMetaInfResources());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
host.addChild(tomcatContext);
|
||||
}
|
||||
|
||||
private void nutzSupport() {
|
||||
// 添加其他starter提供的WebXXXX服务
|
||||
Map<String, WebFilterFace> filters = new HashMap<String, WebFilterFace>();
|
||||
for (Object object : appContext.getStarters()) {
|
||||
if (object instanceof WebFilterFace) {
|
||||
WebFilterFace webFilter = (WebFilterFace) object;
|
||||
if (webFilter == null || webFilter.getFilter() == null) {
|
||||
continue;
|
||||
}
|
||||
filters.put(webFilter.getName(), webFilter);
|
||||
}
|
||||
if (object instanceof WebServletFace) {
|
||||
WebServletFace webServlet = (WebServletFace) object;
|
||||
if (webServlet == null || webServlet.getServlet() == null) {
|
||||
continue;
|
||||
}
|
||||
addServlet(webServlet);
|
||||
}
|
||||
if (object instanceof WebEventListenerFace) {
|
||||
WebEventListenerFace contextListener = (WebEventListenerFace) object;
|
||||
if (contextListener == null || contextListener.getEventListener() == null) {
|
||||
continue;
|
||||
}
|
||||
this.tomcatContext.addApplicationEventListener(contextListener.getEventListener());
|
||||
}
|
||||
}
|
||||
String _filterOrders = conf.get(PROP_WEB_FILTERS_ORDER);
|
||||
if (_filterOrders == null)
|
||||
_filterOrders = "whale,druid,shiro,nutz";
|
||||
else if (_filterOrders.endsWith("+")) {
|
||||
_filterOrders = _filterOrders.substring(0, _filterOrders.length() - 1) + ",whale,druid,shiro,nutz";
|
||||
}
|
||||
String[] filterOrders = Strings.splitIgnoreBlank(_filterOrders);
|
||||
|
||||
for (String filterName : filterOrders) {
|
||||
addFilter(filters.remove(filterName));
|
||||
}
|
||||
for (WebFilterFace webFilter : filters.values()) {
|
||||
addFilter(webFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private void addDefaultServlet() {
|
||||
Wrapper defaultServlet = this.tomcatContext.createWrapper();
|
||||
defaultServlet.setName("default");
|
||||
defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
|
||||
defaultServlet.addInitParameter("debug", "0");
|
||||
defaultServlet.addInitParameter("listings", "false");
|
||||
defaultServlet.setLoadOnStartup(1);
|
||||
defaultServlet.setOverridable(true);
|
||||
this.tomcatContext.addChild(defaultServlet);
|
||||
addServletMapping("/", "default");
|
||||
}
|
||||
|
||||
private void addJspServlet() {
|
||||
Wrapper jspServlet = this.tomcatContext.createWrapper();
|
||||
jspServlet.setName("jsp");
|
||||
jspServlet.setServletClass(JspServlet.class.getName());
|
||||
jspServlet.addInitParameter("fork", "false");
|
||||
jspServlet.addInitParameter("development", "false");
|
||||
jspServlet.setLoadOnStartup(3);
|
||||
this.tomcatContext.addChild(jspServlet);
|
||||
addServletMapping("*.jsp", "jsp");
|
||||
addServletMapping("*.jspx", "jsp");
|
||||
}
|
||||
|
||||
private void addServlet(WebServletFace webServlet) {
|
||||
logger.debugf("[NutzBoot] add servlet name=%s pathSpec=%s", webServlet.getName(), webServlet.getPathSpec());
|
||||
|
||||
Wrapper servlet = tomcatContext.createWrapper();
|
||||
servlet.setName(webServlet.getName());
|
||||
servlet.setServletClass(webServlet.getServlet().getClass().getName());
|
||||
|
||||
for (Map.Entry<String, String> entry : webServlet.getInitParameters().entrySet()) {
|
||||
servlet.addInitParameter(entry.getKey(), entry.getValue());
|
||||
}
|
||||
servlet.setOverridable(true);
|
||||
tomcatContext.addChild(servlet);
|
||||
addServletMapping(webServlet.getPathSpec(), webServlet.getName());
|
||||
}
|
||||
|
||||
private void addFilter(WebFilterFace filterFace) {
|
||||
if (filterFace == null || filterFace.getFilter() == null) {
|
||||
return;
|
||||
}
|
||||
logger.debugf("[NutzBoot] add filter name=%s pathSpec=%s", filterFace.getName(), filterFace.getPathSpec());
|
||||
FilterDef filterDef = new FilterDef();
|
||||
filterDef.setFilter(filterFace.getFilter());
|
||||
filterDef.setFilterName(filterFace.getName());
|
||||
filterDef.setFilterClass(filterFace.getFilter().getClass().getName());
|
||||
if (filterFace.getInitParameters() != null && !filterFace.getInitParameters().isEmpty()) {
|
||||
for (Map.Entry<String, String> entry : filterFace.getInitParameters().entrySet()) {
|
||||
filterDef.addInitParameter(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
this.tomcatContext.addFilterDef(filterDef);
|
||||
}
|
||||
|
||||
private Context findContext() {
|
||||
for (Container child : this.tomcat.getHost().findChildren()) {
|
||||
if (child instanceof Context) {
|
||||
return (Context) child;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("The host does not contain a Context");
|
||||
}
|
||||
|
||||
private void addInstanceIdToEngineName() {
|
||||
int instanceId = containerCounter.incrementAndGet();
|
||||
if (instanceId > 0) {
|
||||
Engine engine = this.tomcat.getEngine();
|
||||
engine.setName(engine.getName() + "-" + instanceId);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeServiceConnectors() {
|
||||
for (Service service : this.tomcat.getServer().findServices()) {
|
||||
Connector[] connectors = service.findConnectors().clone();
|
||||
this.serviceConnectors.put(service, connectors);
|
||||
for (Connector connector : connectors) {
|
||||
service.removeConnector(connector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rethrowDeferredStartupExceptions() {
|
||||
Container[] children = this.tomcat.getHost().findChildren();
|
||||
for (Container container : children) {
|
||||
if (!LifecycleState.STARTED.equals(container.getState())) {
|
||||
throw new IllegalStateException(container + " failed to start");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addPreviouslyRemovedConnectors() {
|
||||
Service[] services = this.tomcat.getServer().findServices();
|
||||
for (Service service : services) {
|
||||
Connector[] connectors = this.serviceConnectors.get(service);
|
||||
if (connectors != null) {
|
||||
for (Connector connector : connectors) {
|
||||
service.addConnector(connector);
|
||||
if (!this.autoStart) {
|
||||
stopProtocolHandler(connector);
|
||||
}
|
||||
}
|
||||
this.serviceConnectors.remove(service);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void stopProtocolHandler(Connector connector) {
|
||||
try {
|
||||
connector.getProtocolHandler().stop();
|
||||
} catch (Exception ex) {
|
||||
logger.error("Cannot pause connector: ", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void startConnector(Connector connector) {
|
||||
try {
|
||||
for (Container child : this.tomcat.getHost().findChildren()) {
|
||||
if (child instanceof TomcatContext) {
|
||||
((TomcatContext) child).deferredLoadOnStartup();
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.error("Cannot start connector: ", ex);
|
||||
throw new EmbeddedServletContainerException(
|
||||
"Unable to start embedded Tomcat connectors", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkThatConnectorsHaveStarted() {
|
||||
for (Connector connector : this.tomcat.getService().findConnectors()) {
|
||||
if (LifecycleState.FAILED.equals(connector.getState())) {
|
||||
throw new ConnectorStartFailedException(connector.getPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void resetDefaultLocaleMapping() {
|
||||
this.tomcatContext.addLocaleEncodingMappingParameter(Locale.ENGLISH.toString(), DEFAULT_CHARSET.displayName());
|
||||
this.tomcatContext.addLocaleEncodingMappingParameter(Locale.FRENCH.toString(), DEFAULT_CHARSET.displayName());
|
||||
}
|
||||
|
||||
private void addServletMapping(String pattern, String name) {
|
||||
this.tomcatContext.addServletMappingDecoded(pattern, name);
|
||||
}
|
||||
|
||||
private void startDaemonAwaitThread() {
|
||||
Thread awaitThread = new Thread("container-" + (containerCounter.get())) {
|
||||
@Override
|
||||
public void run() {
|
||||
TomcatStarter.this.tomcat.getServer().await();
|
||||
}
|
||||
};
|
||||
awaitThread.setContextClassLoader(getClass().getClassLoader());
|
||||
awaitThread.setDaemon(false);
|
||||
awaitThread.start();
|
||||
}
|
||||
|
||||
private void stopSilently() {
|
||||
try {
|
||||
stop();
|
||||
} catch (LifecycleException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private String getPortsDescription(boolean localPort) {
|
||||
StringBuilder ports = new StringBuilder();
|
||||
for (Connector connector : this.tomcat.getService().findConnectors()) {
|
||||
ports.append(ports.length() == 0 ? "" : " ");
|
||||
int port = (localPort ? connector.getLocalPort() : connector.getPort());
|
||||
ports.append(port + " (" + connector.getScheme() + ")");
|
||||
}
|
||||
return ports.toString();
|
||||
}
|
||||
|
||||
|
||||
public Charset getUriEncoding() {
|
||||
return uriEncoding;
|
||||
}
|
||||
|
||||
protected File createTempDir(String prefix) {
|
||||
try {
|
||||
File tempDir = File.createTempFile(prefix + ".", "." + getPort());
|
||||
tempDir.delete();
|
||||
tempDir.mkdir();
|
||||
tempDir.deleteOnExit();
|
||||
return tempDir;
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(
|
||||
"Unable to create tempDir. java.io.tmpdir is set to "
|
||||
+ System.getProperty("java.io.tmpdir"),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static class StoreMergedWebXmlListener implements LifecycleListener {
|
||||
|
||||
private static final String MERGED_WEB_XML = "org.apache.tomcat.util.scan.MergedWebXml";
|
||||
|
||||
@Override
|
||||
public void lifecycleEvent(LifecycleEvent event) {
|
||||
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
|
||||
onStart((Context) event.getLifecycle());
|
||||
}
|
||||
}
|
||||
|
||||
private void onStart(Context context) {
|
||||
ServletContext servletContext = context.getServletContext();
|
||||
if (servletContext.getAttribute(MERGED_WEB_XML) == null) {
|
||||
servletContext.setAttribute(MERGED_WEB_XML, getEmptyWebXml());
|
||||
}
|
||||
}
|
||||
|
||||
private String getEmptyWebXml() {
|
||||
InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("empty-web.xml");
|
||||
Assert.state(stream != null, "Unable to read empty web.xml");
|
||||
try {
|
||||
try {
|
||||
return StreamUtils.copyToString(stream, Charset.forName("UTF-8"));
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Object getNamingToken(Context context) {
|
||||
try {
|
||||
return context.getNamingToken();
|
||||
} catch (NoSuchMethodError ex) {
|
||||
// Use the context itself on Tomcat 7
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package org.nutz.boot.starter.tomcat;
|
||||
|
||||
import org.apache.catalina.loader.WebappClassLoader;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
public class TomcatWebappClassLoader extends WebappClassLoader {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TomcatWebappClassLoader.class);
|
||||
|
||||
public TomcatWebappClassLoader() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TomcatWebappClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException {
|
||||
Class<?> result = findExistingLoadedClass(name);
|
||||
result = (result == null ? doLoadClass(name) : result);
|
||||
if (result == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return resolveIfNecessary(result, resolve);
|
||||
}
|
||||
|
||||
private Class<?> findExistingLoadedClass(String name) {
|
||||
Class<?> resultClass = findLoadedClass0(name);
|
||||
resultClass = (resultClass == null ? findLoadedClass(name) : resultClass);
|
||||
return resultClass;
|
||||
}
|
||||
|
||||
private Class<?> doLoadClass(String name) throws ClassNotFoundException {
|
||||
checkPackageAccess(name);
|
||||
if ((this.delegate || filter(name, true))) {
|
||||
Class<?> result = loadFromParent(name);
|
||||
return (result == null ? findClassIgnoringNotFound(name) : result);
|
||||
}
|
||||
Class<?> result = findClassIgnoringNotFound(name);
|
||||
return (result == null ? loadFromParent(name) : result);
|
||||
}
|
||||
|
||||
private Class<?> resolveIfNecessary(Class<?> resultClass, boolean resolve) {
|
||||
if (resolve) {
|
||||
resolveClass(resultClass);
|
||||
}
|
||||
return (resultClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addURL(URL url) {
|
||||
// Ignore URLs added by the Tomcat 8 implementation (see gh-919)
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Ignoring request to add " + url + " to the tomcat classloader");
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> loadFromParent(String name) {
|
||||
if (this.parent == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Class.forName(name, false, this.parent);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> findClassIgnoringNotFound(String name) {
|
||||
try {
|
||||
return findClass(name);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPackageAccess(String name) throws ClassNotFoundException {
|
||||
if (this.securityManager != null && name.lastIndexOf('.') >= 0) {
|
||||
try {
|
||||
this.securityManager
|
||||
.checkPackageAccess(name.substring(0, name.lastIndexOf('.')));
|
||||
}
|
||||
catch (SecurityException ex) {
|
||||
throw new ClassNotFoundException("Security Violation, attempt to use "
|
||||
+ "Restricted Class: " + name, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
org.nutz.boot.starter.tomcat.TomcatStarter
|
6
nutzboot-starter-tomcat/src/main/resources/empty-web.xml
Normal file
6
nutzboot-starter-tomcat/src/main/resources/empty-web.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
|
||||
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
|
||||
</web-app>
|
47
pom.xml
47
pom.xml
@ -12,6 +12,7 @@
|
||||
<slf4j.version>1.7.25</slf4j.version>
|
||||
<dubbo.version>2.5.6</dubbo.version>
|
||||
<jetty.version>9.4.8.v20171121</jetty.version>
|
||||
<tomcat.version>8.5.23</tomcat.version>
|
||||
<shiro.version>1.3.2</shiro.version>
|
||||
<jetx.version>2.1.5</jetx.version>
|
||||
</properties>
|
||||
@ -254,6 +255,52 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--tomcat start-->
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-el</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-jasper</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-websocket</artifactId>
|
||||
<version>${tomcat.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-logging-juli</artifactId>
|
||||
<version>8.0.28</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-logging-log4j</artifactId>
|
||||
<version>8.0.28</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jdt.core.compiler</groupId>
|
||||
<artifactId>ecj</artifactId>
|
||||
<version>4.4.2</version>
|
||||
</dependency>
|
||||
|
||||
<!--tomcat end-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
|
Loading…
Reference in New Issue
Block a user