All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.antelopeframework.launcher.TomcatBootstrap Maven / Gradle / Ivy

The newest version!
package com.github.antelopeframework.launcher;

import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;

import org.apache.catalina.Host;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardService;
import org.apache.catalina.core.StandardThreadExecutor;
import org.apache.catalina.session.StandardManager;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.ProtocolHandler;

import com.github.antelopeframework.util.component.AbstractLifeCycle;

/**
 * 
 * @author yangzhi
 *
 */
public class TomcatBootstrap extends AbstractLifeCycle {
    private static final Logger logger = Logger.getLogger(TomcatBootstrap.class.getName());

    public final static Integer DEFAULT_WEBAPP_PORT = 8080;
    public final static String DEFAULT_WEBAPP_CONTEXT_PATH = "/";
    public final static String DEFAULT_WEBAPP_LOCATION = "../webapp";
    
    //~ 最基础的配置
    public static final String PARAM_WEBAPP_PORT = "tomcat.webapp.port";
    public static final String PARAM_WEBAPP_CONTEXT_PATH = "tomcat.webapp.context-path";
    public static final String PARAM_WEBAPP_LOCATION = "tomcat.webapp.location";
    
    //~ 线程池配置
    public static final String PARAM_EXECUTOR_NAME = "tomcat.executor.name";
    public static final String PARAM_EXECUTOR_NAME_PREFIX = "tomcat.executor.namePrefix";
    public static final String PARAM_EXECUTOR_MAX_THREAD = "tomcat.executor.maxThreads";
    public static final String PARAM_EXECUTOR_MIN_SPARE_THREADS = "tomcat.executor.minSpareThreads";
    public static final String PARAM_EXECUTOR_MAX_IDLE_TIME = "tomcat.executor.maxIdleTime";
    
    //~ 日志配置
    public static final String PARAM_ACCESS_LOG_DIR = "tomcat.access-log.dir";
    public static final String PARAM_ACCESS_LOG_PATTERN = "tomcat.access-log.pattern";
    public static final String PARAM_ACCESS_LOG_PREFIX = "tomcat.access-log.prefix";
    public static final String PARAM_ACCESS_LOG_SUFFIX = "tomcat.access-log.suffix";
    public static final String PARAM_ACCESS_LOG_ROTATABLE = "tomcat.access-log.rotatable";
    
    public final static String PARAM_WEB_EXTRA_RESOURCE_PATHS = "tomcat.extra-resource-paths";
    
    protected final Properties tomcatConfigs;

    protected Tomcat tomcatServer;
    protected StandardContext ctx;
    protected int defaultPort;
    
    protected StandardThreadExecutor defaultThreadExecutor;
    
    int listenPort = 0;
    
    protected TomcatBootstrap() throws Exception {
        tomcatConfigs = new Properties();
        try {
            tomcatConfigs.load(TomcatBootstrap.class.getResourceAsStream("/tomcat.properties"));
        } catch (Exception e) {
            
        }
    }

    @Override
    public void doStart() throws Exception {
        int port = intValue(tomcatConfigs, PARAM_WEBAPP_PORT, DEFAULT_WEBAPP_PORT);
        
        ServerSocket socket = null;
        try {
            socket = new ServerSocket(port);
        } catch (IOException e) {
            logger.log(Level.SEVERE, "port " + port + " is already in use. please try another port.");
            throw e;
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
        
        defaultPort = port;
        
        logger.log(Level.INFO, "Tomcat version=" + ServerInfo.getServerInfo());

        tomcatServer = new Tomcat();
        tomcatServer.setPort(defaultPort);
        
        initConnectors();
        
        ClassLoader cl = TomcatBootstrap.class.getClassLoader();
        logger.log(Level.INFO, "setParentClassLoader: " + cl);
        tomcatServer.getEngine().setParentClassLoader(cl);
        tomcatServer.getHost().setParentClassLoader(cl);
        tomcatServer.getServer().setParentClassLoader(cl);
        tomcatServer.getService().setParentClassLoader(cl);
        
        
        initAccessLog();
        initWebApp();
        
        StandardManager manager = new StandardManager();
        manager.setPathname(null);
        ctx.setManager(manager);
        
        try {
            tomcatServer.start();
            tomcatServer.getServer().await();
        } catch (Throwable e) {
            System.exit(0);
        } finally {
            stop();
        }
    }

    @Override
    public void doStop() throws Exception {
        try {
            if (tomcatServer != null) {
                if (tomcatServer.getServer() != null && tomcatServer.getServer().getState() != LifecycleState.DESTROYED) {
                    if (tomcatServer.getServer().getState() != LifecycleState.STOPPED) {
                        tomcatServer.stop();
                    }
                }

                tomcatServer.destroy();
            }
        } catch (Throwable e) {
            e.printStackTrace();
            logger.log(Level.SEVERE, e.getMessage());
        }
    }

    protected void initConnectors() throws IOException, SocketException {
        StandardService service = (StandardService) tomcatServer.getService();
        
        //~ 默认线程池
        this.defaultThreadExecutor = new StandardThreadExecutor();
        
        this.defaultThreadExecutor.setName(stringValue(tomcatConfigs, PARAM_EXECUTOR_NAME, "tomcatThreadPool"));
        this.defaultThreadExecutor.setNamePrefix(stringValue(tomcatConfigs, PARAM_EXECUTOR_NAME_PREFIX, "catalina-exec-"));
        this.defaultThreadExecutor.setMaxThreads(intValue(tomcatConfigs, PARAM_EXECUTOR_MAX_THREAD, 200));
        this.defaultThreadExecutor.setMinSpareThreads(intValue(tomcatConfigs, PARAM_EXECUTOR_MIN_SPARE_THREADS, 25));
        this.defaultThreadExecutor.setMaxIdleTime(intValue(tomcatConfigs, PARAM_EXECUTOR_MAX_IDLE_TIME, 60000));
        
        service.addExecutor(this.defaultThreadExecutor);
        
        //~ 默认连接器
        tomcatServer.setConnector(new Connector("HTTP/1.1"));
        initDefaultConnector(service, this.defaultThreadExecutor, null);
    }

    protected void initAccessLog() {
        String dir = stringValue(tomcatConfigs, PARAM_ACCESS_LOG_DIR, null);
        if (isBlank(dir)) {
            String logDir = System.getProperty("logDir");
            String appName = System.getProperty("appName");
            
            if (!isBlank(logDir) && !isBlank(appName)) {
                dir = logDir + "/" + appName;
            }
        }
        
        if (isBlank(dir)) {
            logger.log(Level.INFO, "access log dir is empty");
            return;
        }
        
        AccessLogValve al = new AccessLogValve();
        
        File logFile = new File(dir);
        if (!logFile.exists() || !logFile.isDirectory()) {
            logFile.mkdirs();
        }
        
        logger.log(Level.SEVERE, "access log dir=" + logFile.getAbsolutePath());
        
        al.setDirectory(logFile.getAbsolutePath());
        
        String pattern = stringValue(tomcatConfigs, PARAM_ACCESS_LOG_PATTERN, "%h %l %u %t %r %s %b");
        if (pattern != null) {
            al.setPattern(pattern);
        }
        
        String prefix = stringValue(tomcatConfigs, PARAM_ACCESS_LOG_PREFIX, "access-log.");
        if (prefix != null) {
            al.setPrefix(prefix);
        }
        
        String suffix = stringValue(tomcatConfigs, PARAM_ACCESS_LOG_SUFFIX, null);
        if (suffix != null) {
            al.setSuffix(suffix);
        }
        
        String rotate = stringValue(tomcatConfigs, PARAM_ACCESS_LOG_ROTATABLE, "true");
        if (rotate != null) {
            al.setRotatable(Boolean.valueOf(rotate));
        }
        
        ((StandardEngine) tomcatServer.getEngine()).addValve(al);
    }
    
    private void initDefaultConnector(StandardService service, StandardThreadExecutor executor, InetAddress loopbackAddress) {
        listenPort = 0;
        final Connector connector = new Connector("HTTP/1.1");
        setExecutor(connector, executor);

        enableCompression(connector);
        connector.setPort(defaultPort);
        
        connector.setURIEncoding("UTF-8");
        connector.setUseBodyEncodingForURI(true);
        
        if (loopbackAddress != null) {
            String address = loopbackAddress.getHostAddress();
            connector.setAttribute("address", address);
            logger.log(Level.INFO, "bind address=" + address);
        }

        connector.setAttribute("bindOnInit", "true");
        service.addConnector(connector);
    }
    
    protected void initWebApp() throws IOException, ServletException {
        Host host = tomcatServer.getHost();
        String appbase = new File(stringValue(tomcatConfigs, PARAM_WEBAPP_LOCATION, DEFAULT_WEBAPP_LOCATION)).getCanonicalPath();
        host.setAppBase(appbase);
        
        String contextPath = stringValue(tomcatConfigs, PARAM_WEBAPP_CONTEXT_PATH, "/");
        ctx = (StandardContext) tomcatServer.addWebapp(contextPath, appbase);
        
//        ctx.setLoader(new WebappLoader(Thread.currentThread().getContextClassLoader()));
        
        String extraResourcePaths = stringValue(tomcatConfigs, PARAM_WEB_EXTRA_RESOURCE_PATHS, "");
        if (extraResourcePaths != null && !"".equals(extraResourcePaths.trim())) {
        	String[] paths = extraResourcePaths.split(";");
        	for (String one : paths) {
        		String[] parts = one.split("=");
        		File additionWebResource = new File(parts[1]);
        		WebResourceRoot resources = new StandardRoot(ctx);
        		resources.addPreResources(new DirResourceSet(resources, parts[0], additionWebResource.getAbsolutePath(), "/"));
        		ctx.setResources(resources);
        	}
        }
    }
    
    protected Collection getLoopbackAddresses() throws SocketException {
        ArrayList addresses = new ArrayList();
        Enumeration nets = NetworkInterface.getNetworkInterfaces();
        for (NetworkInterface netint : Collections.list(nets)) {
            if (netint.isLoopback()) {
                Enumeration inetAddresses = netint.getInetAddresses();
                for (InetAddress inetAddress : Collections.list(inetAddresses)) {
                    addresses.add(inetAddress);
                }
                
                break;
            }
        }

        return addresses;
    }
    
    protected void enableCompression(Connector connector) {
        connector.setProperty("compression", "on");
        connector.setProperty("compressableMimeType", "text/html,text/xml,text/plain,text/javascript,application/javascript");
    }
    
    @SuppressWarnings("rawtypes")
    protected void setExecutor(Connector con, Executor executor) {
        ProtocolHandler protocol = con.getProtocolHandler();
        if (protocol instanceof AbstractProtocol) {
            ((AbstractProtocol) protocol).setExecutor(executor);;
        } 
    }
    
    /**
     * 优先使用System.get()
     * 
     * @param properties
     * @param key
     * @return
     */
    protected static String stringValue(Properties properties, String key, String defaultValue) {
        Object value = System.getProperty(key);
        if (isBlank(value)) {
            value = properties.get(key);
        }
        
        if (!isBlank(value)) {
            return StringUtils.trim(value.toString());
        }
        
        return defaultValue;
    }

    /**
     * 获取整数类型结果. 优先使用System.get().
     * 
     * @param properties
     * @param key
     * @param defaultValue
     * @return
     */
    protected Integer intValue(Properties properties, String key, Integer defaultValue) {
        String value = stringValue(properties, key, null);

        Integer result = null;

        try {
            result = Integer.valueOf(value);
        } catch (Exception e) {
            result = defaultValue;
        }

        return result;
    }

    /**
     * 判断字符串是否为空串(null, '', ' '均为空串).
     * 
     * @param value
     * @return
     */
    protected static Boolean isBlank(Object value) {
        return value == null || StringUtils.isBlank(value.toString());
    }
    
    
    public static void main(String[] args) throws Exception {
        final TomcatBootstrap server = new TomcatBootstrap();
        try {

            Runtime.getRuntime().addShutdownHook(new Thread() {
                public void run() {
                    try {
                        server.stop();
                        logger.info("Server stopped!");
                    } catch (Throwable t) {
                        t.printStackTrace();
                        logger.log(Level.SEVERE, t.getMessage());
                    }

                    synchronized (TomcatBootstrap.class) {
                        TomcatBootstrap.class.notify();
                    }
                }
            });

            server.start();
        } catch (Exception e) {
            e.printStackTrace();
            logger.log(Level.SEVERE, e.getMessage());
            System.exit(1);
        }

        synchronized (TomcatBootstrap.class) {
            while (server.isRunning()) {
                try {
                    TomcatBootstrap.class.wait();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    @Override
    public String toString() {
        return new ToStringBuilder(this)
            .append(tomcatConfigs)
            .toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy