Java tutorial
/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2013,2014,2015 GAEL Systems * * This file is part of DHuS software sources. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.server.http; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.logging.LogManager; import javax.servlet.Servlet; import javax.servlet.ServletException; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; import org.apache.catalina.connector.Connector; import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardEngine; import org.apache.catalina.startup.Catalina; import org.apache.catalina.startup.Constants; import org.apache.catalina.startup.ContextConfig; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.Tomcat.DefaultWebXmlListener; import org.apache.catalina.valves.RemoteAddrValve; import org.apache.catalina.valves.RemoteIpValve; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.juli.ClassLoaderLogManager; import org.apache.tomcat.util.ExceptionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.google.common.io.Files; import fr.gael.dhus.server.http.web.WebApplication; import fr.gael.dhus.server.http.web.WebServlet; import fr.gael.dhus.system.config.ConfigurationManager; @Component public class TomcatServer { private static Log logger = LogFactory.getLog(TomcatServer.class); @Autowired private ConfigurationManager configurationManager; private String tomcatpath; private Catalina cat; private ArrayList<StandardContext> contexts = new ArrayList<StandardContext>(); /** * Initialize Tomcat inner datasets. */ public void init() throws TomcatException { tomcatpath = configurationManager.getTomcatConfiguration().getPath(); final String extractDirectory = tomcatpath; File extractDirectoryFile = new File(extractDirectory); logger.info("Starting tomcat in " + extractDirectoryFile.getPath()); try { extract(extractDirectoryFile, extractDirectory); // create tomcat various paths new File(extractDirectory, "conf").mkdirs(); File cfg = new File(ClassLoader.getSystemResource("server.xml").toURI()); Files.copy(cfg, new File(extractDirectory, "conf/server.xml")); new File(extractDirectory, "logs").mkdirs(); new File(extractDirectory, "webapps").mkdirs(); new File(extractDirectory, "work").mkdirs(); File tmpDir = new File(extractDirectory, "temp"); tmpDir.mkdirs(); System.setProperty("java.io.tmpdir", tmpDir.getAbsolutePath()); System.setProperty("catalina.base", extractDirectoryFile.getAbsolutePath()); System.setProperty("catalina.home", extractDirectoryFile.getAbsolutePath()); cat = new Catalina(); } catch (Exception e) { throw new TomcatException("Cannot initalize Tomcat environment.", e); } Runtime.getRuntime().addShutdownHook(new TomcatShutdownHook()); } /** * This method Starts the Tomcat server. */ public void start() throws TomcatException { if (cat == null) init(); cat.start(); } /** * This method Stops the Tomcat server. */ public void stop() throws TomcatException { // Stop the embedded server cat.stop(); cat = null; } public boolean isRunning() { return cat != null; } protected class TomcatShutdownHook extends Thread { protected TomcatShutdownHook() { } @Override public void run() { try { TomcatServer.this.stop(); } catch (Throwable ex) { ExceptionUtils.handleThrowable(ex); logger.error("Fail to properly shutdown Tomcat:" + ex.getMessage()); } finally { LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).shutdown(); } } } } protected void extract(File extract_directory_file, String extract_directory) throws Exception { if (extract_directory_file.exists()) { logger.debug("Clean extractDirectory"); FileUtils.deleteDirectory(extract_directory_file); } if (!extract_directory_file.exists()) { boolean created = extract_directory_file.mkdirs(); if (!created) { throw new Exception("FATAL: impossible to create directory:" + extract_directory_file.getPath()); } } // ensure webapp dir is here boolean created = new File(extract_directory, "webapps").mkdirs(); if (!created) { throw new Exception( "FATAL: impossible to create directory:" + extract_directory_file.getPath() + "/webapps"); } expandConfigurationFile("web.xml", extract_directory_file); } private static void expandConfigurationFile(String file_name, File extract_directory) throws Exception { InputStream inputStream = null; try { inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("conf/" + file_name); if (inputStream != null) { File confDirectory = new File(extract_directory, "conf"); if (!confDirectory.exists()) { confDirectory.mkdirs(); } expand(inputStream, new File(confDirectory, file_name)); } } finally { if (inputStream != null) { inputStream.close(); } } } private static void expand(InputStream input, File file) throws IOException { BufferedOutputStream output = null; try { output = new BufferedOutputStream(new FileOutputStream(file)); byte buffer[] = new byte[2048]; while (true) { int n = input.read(buffer); if (n <= 0) { break; } output.write(buffer, 0, n); } } finally { if (output != null) { try { output.close(); } catch (IOException e) { // Ignore } } } } public void install(WebApplication web_application) throws TomcatException { try { String folder = web_application.getName() == "" ? "ROOT" : web_application.getName(); if (web_application.hasWarStream()) { InputStream stream = web_application.getWarStream(); if (stream == null) { throw new TomcatException("Cannot install WebApplication " + web_application.getName() + ". The referenced war " + "file does not exist."); } JarInputStream jis = new JarInputStream(stream); File destDir = new File(tomcatpath, "webapps/" + folder); byte[] buffer = new byte[4096]; JarEntry file; while ((file = jis.getNextJarEntry()) != null) { File f = new File(destDir + java.io.File.separator + file.getName()); if (file.isDirectory()) { // if its a directory, create it f.mkdirs(); continue; } if (!f.getParentFile().exists()) { f.getParentFile().mkdirs(); } java.io.FileOutputStream fos = new java.io.FileOutputStream(f); int read; while ((read = jis.read(buffer)) != -1) { fos.write(buffer, 0, read); } fos.flush(); fos.close(); } jis.close(); } web_application.configure(new File(tomcatpath, "webapps/" + folder).getPath()); StandardEngine engine = (StandardEngine) cat.getServer().findServices()[0].getContainer(); Container container = engine.findChild(engine.getDefaultHost()); StandardContext ctx = new StandardContext(); String url = (web_application.getName() == "" ? "" : "/") + web_application.getName(); ctx.setName(url); ctx.setPath(url); ctx.setDocBase(new File(tomcatpath, "webapps/" + folder).getPath()); ctx.addLifecycleListener(new DefaultWebXmlListener()); ctx.setConfigFile(getWebappConfigFile(new File(tomcatpath, "webapps/" + folder).getPath(), url)); ContextConfig ctxCfg = new ContextConfig(); ctx.addLifecycleListener(ctxCfg); ctxCfg.setDefaultWebXml("fr/gael/dhus/server/http/global-web.xml"); container.addChild(ctx); contexts.add(ctx); List<WebServlet> servlets = web_application.getServlets(); for (WebServlet servlet : servlets) { addServlet(ctx, servlet.getServletName(), servlet.getUrlPattern(), servlet.getServlet(), servlet.isLoadOnStartup()); } List<String> welcomeFiles = web_application.getWelcomeFiles(); for (String welcomeFile : welcomeFiles) { ctx.addWelcomeFile(welcomeFile); } if (web_application.getAllow() != null || web_application.getDeny() != null) { RemoteIpValve valve = new RemoteIpValve(); valve.setRemoteIpHeader("x-forwarded-for"); valve.setProxiesHeader("x-forwarded-by"); valve.setProtocolHeader("x-forwarded-proto"); ctx.addValve(valve); RemoteAddrValve valve_addr = new RemoteAddrValve(); valve_addr.setAllow(web_application.getAllow()); valve_addr.setDeny(web_application.getDeny()); ctx.addValve(valve_addr); } } catch (Exception e) { throw new TomcatException("Cannot install service", e); } } public void install(fr.gael.dhus.server.http.WebApplication web_application) throws TomcatException { logger.info("Installing webapp " + web_application); String appName = web_application.getName(); String folder; if (appName.trim().isEmpty()) { folder = "ROOT"; } else { folder = appName; } try { if (web_application.hasWarStream()) { InputStream stream = web_application.getWarStream(); if (stream == null) { throw new TomcatException("Cannot install webApplication " + web_application.getName() + ". The referenced war file does not exist."); } JarInputStream jis = new JarInputStream(stream); File destDir = new File(tomcatpath, "webapps/" + folder); byte[] buffer = new byte[4096]; JarEntry file; while ((file = jis.getNextJarEntry()) != null) { File f = new File(destDir + java.io.File.separator + file.getName()); if (file.isDirectory()) { // if its a directory, create it f.mkdirs(); continue; } if (!f.getParentFile().exists()) { f.getParentFile().mkdirs(); } java.io.FileOutputStream fos = new java.io.FileOutputStream(f); int read; while ((read = jis.read(buffer)) != -1) { fos.write(buffer, 0, read); } fos.flush(); fos.close(); } jis.close(); } web_application.configure(new File(tomcatpath, "webapps/" + folder).getPath()); StandardEngine engine = (StandardEngine) cat.getServer().findServices()[0].getContainer(); Container container = engine.findChild(engine.getDefaultHost()); StandardContext ctx = new StandardContext(); String url = (web_application.getName() == "" ? "" : "/") + web_application.getName(); ctx.setName(url); ctx.setPath(url); ctx.setDocBase(new File(tomcatpath, "webapps/" + folder).getPath()); ctx.addLifecycleListener(new DefaultWebXmlListener()); ctx.setConfigFile(getWebappConfigFile(new File(tomcatpath, "webapps/" + folder).getPath(), url)); ContextConfig ctxCfg = new ContextConfig(); ctx.addLifecycleListener(ctxCfg); ctxCfg.setDefaultWebXml("fr/gael/dhus/server/http/global-web.xml"); container.addChild(ctx); List<String> welcomeFiles = web_application.getWelcomeFiles(); for (String welcomeFile : welcomeFiles) { ctx.addWelcomeFile(welcomeFile); } if (web_application.getAllow() != null || web_application.getDeny() != null) { RemoteIpValve valve = new RemoteIpValve(); valve.setRemoteIpHeader("x-forwarded-for"); valve.setProxiesHeader("x-forwarded-by"); valve.setProtocolHeader("x-forwarded-proto"); ctx.addValve(valve); RemoteAddrValve valve_addr = new RemoteAddrValve(); valve_addr.setAllow(web_application.getAllow()); valve_addr.setDeny(web_application.getDeny()); ctx.addValve(valve_addr); } web_application.checkInstallation(); } catch (Exception e) { throw new TomcatException("Cannot install webApplication " + web_application.getName(), e); } } public void install(WebServlet web_servlet) { Context ctx = findContext(web_servlet.getUrlBase()); addServlet(ctx, web_servlet.getServletName(), web_servlet.getUrlPattern(), web_servlet.getServlet(), web_servlet.isLoadOnStartup()); } private Wrapper addServlet(Context ctx, String servlet_name, String url_mapping, Servlet servlet, boolean load_on_startup) { Wrapper wrapper = Tomcat.addServlet(ctx, servlet_name, servlet); ctx.addServletMapping(url_mapping, servlet_name); if (load_on_startup) { try { wrapper.load(); } catch (ServletException e) { e.printStackTrace(); } } return wrapper; } public void await() { cat.getServer().await(); } private Context findContext(String context_name) { StandardEngine engine = (StandardEngine) cat.getServer().findServices()[0].getContainer(); Container container = engine.findChild(engine.getDefaultHost()); StandardContext context = (StandardContext) (container.findChild(context_name)); if (context != null) { return context; } context = new StandardContext(); context.setName(context_name); context.setPath(context_name); context.setDocBase(""); context.addLifecycleListener(new DefaultWebXmlListener()); ContextConfig ctxCfg = new ContextConfig(); context.addLifecycleListener(ctxCfg); // prevent it from looking ( if it finds one - it'll have dup error ) ctxCfg.setDefaultWebXml(Constants.NoDefaultWebXml); container.addChild(context); contexts.add(context); return context; } public int getPort() { Connector connector = cat.getServer().findServices()[0].findConnectors()[0]; return connector.getPort(); } public String getPath() { return this.tomcatpath; } protected URL getWebappConfigFile(String path, String url) { File docBase = new File(path); if (docBase.isDirectory()) { return getWebappConfigFileFromDirectory(docBase, url); } else { return getWebappConfigFileFromJar(docBase, url); } } private URL getWebappConfigFileFromDirectory(File docBase, String url) { URL result = null; File webAppContextXml = new File(docBase, Constants.ApplicationContextXml); if (webAppContextXml.exists()) { try { result = webAppContextXml.toURI().toURL(); } catch (MalformedURLException e) { logger.warn("Unable to determine web application context.xml " + docBase, e); } } return result; } private URL getWebappConfigFileFromJar(File docBase, String url) { URL result = null; JarFile jar = null; try { jar = new JarFile(docBase); JarEntry entry = jar.getJarEntry(Constants.ApplicationContextXml); if (entry != null) { result = new URL("jar:" + docBase.toURI().toString() + "!/" + Constants.ApplicationContextXml); } } catch (IOException e) { logger.warn("Unable to determine web application context.xml " + docBase, e); } finally { if (jar != null) { try { jar.close(); } catch (IOException e) { // ignore } } } return result; } }