com.brienwheeler.apps.tomcat.TomcatBean.java Source code

Java tutorial

Introduction

Here is the source code for com.brienwheeler.apps.tomcat.TomcatBean.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2013 Brien L. Wheeler (brienwheeler@yahoo.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.brienwheeler.apps.tomcat;

import java.io.*;
import java.net.URL;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfoBuilder;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Required;

public class TomcatBean implements InitializingBean, DisposableBean {
    private static final Log log = LogFactory.getLog(TomcatBean.class);

    private static final Pattern KEY_PATTERN = Pattern
            .compile("-+BEGIN (.*)PRIVATE KEY-+([^-]*)-+END .*PRIVATE KEY-+");
    private static final Pattern CERT_PATTERN = Pattern.compile("-+BEGIN CERTIFICATE-+([^-]*)-+END CERTIFICATE-+");

    private static final String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    private int port;
    private int sslPort;
    private File sslKeyFile;
    private File sslCertFile;
    private String baseDirectory;
    private String webAppBase = null;
    private String contextRoot = null;
    private final Tomcat tomcat;

    public TomcatBean() {
        tomcat = new Tomcat();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        tomcat.setBaseDir(baseDirectory);
        tomcat.getHost().setAppBase(baseDirectory);
        configureNetwork();
        extractWarFile();
        tomcat.start();
    }

    @Override
    public void destroy() throws Exception {
        tomcat.stop();
        tomcat.getServer().await();
    }

    @Required
    public void setBaseDirectory(String baseDirectory) {
        this.baseDirectory = baseDirectory;
    }

    @Required
    public void setWebAppBase(String webAppBase) {
        this.webAppBase = webAppBase;
    }

    public void setContextRoot(String contextRoot) {
        this.contextRoot = contextRoot;
    }

    @Required
    public void setPort(int port) {
        this.port = port;
    }

    @Required
    public void setSslPort(int sslPort) {
        this.sslPort = sslPort;
    }

    @Required
    public void setSslKeyFile(File sslKeyFile) {
        this.sslKeyFile = sslKeyFile;
    }

    @Required
    public void setSslCertFile(File sslCertFile) {
        this.sslCertFile = sslCertFile;
    }

    private void configureNetwork() throws Exception {
        if (port > 0) {
            tomcat.setPort(port);
        } else {
            tomcat.getService().removeConnector(tomcat.getConnector());
        }

        if (sslPort > 0) {
            StringBuffer randomPass = new StringBuffer();
            for (int i = 0; i < 10; i++)
                randomPass.append(characters.charAt((int) (characters.length() * Math.random())));
            String keystorePass = randomPass.toString();

            RSAPrivateKey privateKey = readKeyFile();
            log.info("successfully read SSL private key from " + sslKeyFile.getAbsolutePath());
            X509Certificate certificate = readCertFile();
            log.info("successfully read SSL certificate from " + sslCertFile.getAbsolutePath());

            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(null);
            keyStore.setCertificateEntry("cert-alias", certificate);
            keyStore.setKeyEntry("key-alias", privateKey, keystorePass.toCharArray(),
                    new Certificate[] { certificate });
            File keyStoreFile = new File("tcks");
            keyStore.store(new FileOutputStream(keyStoreFile), keystorePass.toCharArray());

            Connector sslConnector = new Connector();
            sslConnector.setPort(sslPort);
            sslConnector.setSecure(true);
            sslConnector.setScheme("https");
            sslConnector.setAttribute("keystoreFile", keyStoreFile.getAbsolutePath());
            sslConnector.setAttribute("keystorePass", keystorePass);
            sslConnector.setAttribute("clientAuth", "false");
            sslConnector.setAttribute("sslProtocol", "TLS");
            sslConnector.setAttribute("SSLEnabled", true);
            tomcat.getService().addConnector(sslConnector);
        }
    }

    private void extractWarFile() {
        if (webAppBase != null && webAppBase.length() > 0) {
            ProtectionDomain protectionDomain = this.getClass().getProtectionDomain();
            URL location = protectionDomain.getCodeSource().getLocation();
            log.info("detected run JAR at " + location);

            if (!location.toExternalForm().startsWith("file:") || !location.toExternalForm().endsWith(".jar"))
                throw new IllegalArgumentException("invalid code location: " + location);

            try {
                ZipFile zipFile = new ZipFile(new File(location.toURI()));

                Enumeration<? extends ZipEntry> entryEnum = zipFile.entries();
                ZipEntry warEntry = null;
                while (entryEnum.hasMoreElements()) {
                    ZipEntry entry = entryEnum.nextElement();
                    String entryName = entry.getName();
                    if (entryName.startsWith(webAppBase) && entryName.endsWith(".war")) {
                        warEntry = entry;
                        break;
                    }
                }

                if (warEntry == null)
                    throw new RuntimeException("can't find JAR entry for " + webAppBase + "*.war");

                log.info("extracting WAR file " + warEntry.getName());

                // extract web app WAR to current directory
                InputStream inputStream = zipFile.getInputStream(warEntry);
                OutputStream outputStream = new FileOutputStream(new File(warEntry.getName()));
                byte buf[] = new byte[1024];
                int nread;
                while ((nread = inputStream.read(buf, 0, 1024)) > 0) {
                    outputStream.write(buf, 0, nread);
                }
                outputStream.close();
                inputStream.close();
                zipFile.close();

                String launchContextRoot = contextRoot != null ? contextRoot : webAppBase;
                if (!launchContextRoot.startsWith("/"))
                    launchContextRoot = "/" + launchContextRoot;

                log.info("launching WAR file " + warEntry.getName() + " at context root " + launchContextRoot);

                // add web app to Tomcat
                Context context = tomcat.addWebapp(launchContextRoot, warEntry.getName());
                if (context instanceof StandardContext)
                    ((StandardContext) context).setUnpackWAR(false);
            } catch (Exception e) {
                throw new RuntimeException("error extracting WAR file", e);
            }
        }
    }

    private RSAPrivateKey readKeyFile() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        String parse[] = readPEMFile(sslKeyFile, KEY_PATTERN, 2);
        if (parse == null)
            throw new IllegalArgumentException("invalid key file contents");

        if (parse[0].length() == 0) { // BEGIN PRIVATE KEY
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return (RSAPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(parse[1])));
        }

        if (parse[0].contains("RSA")) { // BEGIN RSA PRIVATE KEY
            Security.addProvider(new BouncyCastleProvider());

            PEMParser pemParser = new PEMParser(new FileReader(sslKeyFile));
            Object parsedObject = pemParser.readObject();
            if (!(parsedObject instanceof PEMKeyPair))
                throw new IllegalArgumentException("invalid key file contents");

            PEMKeyPair keyPair = (PEMKeyPair) parsedObject;
            RSAPrivateKey privateKey = (RSAPrivateKey) BouncyCastleProvider
                    .getPrivateKey(keyPair.getPrivateKeyInfo());
            if (privateKey == null)
                throw new IllegalArgumentException("invalid key file contents");
            return privateKey;
        }

        throw new IllegalArgumentException("invalid key file contents");
    }

    private X509Certificate readCertFile() throws IOException, CertificateException {
        String parse[] = readPEMFile(sslCertFile, CERT_PATTERN, 1);
        if (parse == null)
            throw new IllegalArgumentException("invalid certificate file contents");
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        return (X509Certificate) certificateFactory
                .generateCertificate(new ByteArrayInputStream(Base64.decode(parse[0])));
    }

    private String[] readPEMFile(File inFile, Pattern pattern, int groups) throws IOException {
        StringBuffer buffer = new StringBuffer();

        BufferedReader reader = new BufferedReader(new FileReader(inFile));
        for (String line = reader.readLine(); line != null; line = reader.readLine())
            buffer.append(line);
        reader.close();

        Matcher matcher = pattern.matcher(buffer.toString());
        if (!matcher.matches())
            return null;

        String[] ret = new String[groups];
        for (int i = 0; i < groups; i++)
            ret[i] = matcher.group(i + 1);
        return ret;
    }
}