001/* 002 * #%L 003 * Netarchivesuite - common 004 * %% 005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023package dk.netarkivet.common.webinterface; 024 025import java.io.File; 026 027import javax.servlet.ServletException; 028 029import org.apache.catalina.core.StandardContext; 030import org.apache.catalina.core.StandardHost; 031import org.apache.catalina.startup.Tomcat; 032import org.apache.tomcat.util.scan.Constants; 033import org.apache.tomcat.util.scan.StandardJarScanFilter; 034 035 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039import dk.netarkivet.common.CommonSettings; 040import dk.netarkivet.common.exceptions.IOFailure; 041import dk.netarkivet.common.utils.CleanupIF; 042import dk.netarkivet.common.utils.FileUtils; 043import dk.netarkivet.common.utils.Settings; 044import dk.netarkivet.common.utils.StringUtils; 045 046/** 047 * A class representing an HttpServer. This class loads web applications as given in settings. 048 */ 049public class GUIWebServer implements CleanupIF { 050 /** 051 * The unique instance of this class. 052 */ 053 private static GUIWebServer instance; 054 /** 055 * The Tomcat server. 056 */ 057 private Tomcat server; 058 /** 059 * Logger for this class. 060 */ 061 private static final Logger log = LoggerFactory.getLogger(GUIWebServer.class); 062 063 /** The lower limit of acceptable HTTP port-numbers. */ 064 private static final int HTTP_PORT_NUMBER_LOWER_LIMIT = 1025; 065 066 /** The upper limit of acceptable HTTP port-numbers. */ 067 private static final int HTTP_PORT_NUMBER_UPPER_LIMIT = 65535; 068 069 /** 070 * Initialises a GUI Web Server and adds web applications. 071 * 072 * @throws IOFailure on trouble starting server. 073 */ 074 public GUIWebServer() { 075 // Read and log settings. 076 077 int port = Integer.parseInt(Settings.get(CommonSettings.HTTP_PORT_NUMBER)); 078 if (port < HTTP_PORT_NUMBER_LOWER_LIMIT || port > HTTP_PORT_NUMBER_UPPER_LIMIT) { 079 throw new IOFailure("Port must be in the range [" + HTTP_PORT_NUMBER_LOWER_LIMIT + ", " 080 + HTTP_PORT_NUMBER_UPPER_LIMIT + "], not " + port); 081 } 082 // TODO Replace with just one setting. See issue NAS-1687 083 String[] webApps = Settings.getAll(CommonSettings.SITESECTION_WEBAPPLICATION); 084 String[] classes = Settings.getAll(CommonSettings.SITESECTION_CLASS); 085 if (webApps.length != classes.length) { 086 throw new IOFailure("Number of webapplications and number of classes defining " 087 + "the webapps do not match. " + "Webapps: [" + StringUtils.conjoin(",", webApps) + "]. " 088 + "]. Classes: [" + StringUtils.conjoin(",", classes) + "]"); 089 } 090 091 log.info("Starting webserver. Port: " + port + " deployment directories: '" + StringUtils.conjoin(",", webApps) 092 + "' classes: '" + StringUtils.conjoin(",", classes) + "'"); 093 094 // Get a tomcat server. 095 server = new Tomcat(); 096 097 098 System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true"); 099 100 // Use directory in commontempdir for cache 101 final File tempDir = FileUtils.getTempDir(); 102 log.debug("GUI using tempdir " + tempDir.getAbsolutePath()); 103 File basedir = tempDir.getAbsoluteFile().getParentFile(); 104 if(tempDir.isAbsolute()) { 105 basedir = new File(""); 106 } 107 log.debug("GUI using basedir " + basedir.getAbsolutePath()); 108 server.setBaseDir(basedir.getAbsolutePath()); 109 110 //File webapps = new File(basedir, "/webapps"); 111 File webapps = tempDir; 112 if (webapps.exists()) { 113 FileUtils.removeRecursively(webapps); 114 log.info("Deleted existing tempdir '" + webapps.getAbsolutePath() + "'"); 115 } 116 117 webapps.mkdirs(); 118 server.getHost().setAppBase(webapps.getAbsolutePath()); 119 server.getHost().setAutoDeploy(true); 120 ((StandardHost) server.getHost()).setUnpackWARs(true); 121 //set the port on which tomcat should run 122 server.setPort(port); 123 boolean taglibsScanningDisabled = false; 124 125 if (System.getProperty(Constants.SKIP_JARS_PROPERTY) == null) { 126 log.info("Scanning for taglibs is disabled as " + Constants.SKIP_JARS_PROPERTY + " is unset."); // Only log this once for all contexts 127 taglibsScanningDisabled = true; 128 } 129 130 //add webapps to tomcat 131 for (String webapp: webApps) { 132 // Construct webbase from the name of the webapp. 133 // (1) If the webapp is webpages/History, the webbase is /History 134 // (2) If the webapp is webpages/History.war, the webbase is /History 135 String webappFilename = new File(webapp).getName(); 136 String webbase = "/" + webappFilename; 137 final String warSuffix = ".war"; 138 if (webappFilename.toLowerCase().endsWith(warSuffix)) { 139 webbase = "/" + webappFilename.substring(0, webappFilename.length() - warSuffix.length()); 140 } 141 142 for (SiteSection section : SiteSection.getSections()) { 143 if (webbase.equals("/" + section.getDirname())) { 144 section.initialize(); 145 break; 146 } 147 } 148 try { 149 //add the jar file to tomcat 150 final File warfileFile = new File(basedir.getAbsolutePath(), webapp); 151 if (!warfileFile.exists() || !warfileFile.isFile()) { 152 throw new IOFailure("Could not find expected file " + warfileFile.getAbsolutePath()); 153 } 154 String warfile = warfileFile.getAbsolutePath(); 155 log.info("Deploying webapp with context {} at docbase {}.", webbase, warfile); 156 StandardContext ctx = (StandardContext) server.addWebapp(webbase, warfile); 157 if (taglibsScanningDisabled) { 158 StandardJarScanFilter jarScanFilter = (StandardJarScanFilter) ctx.getJarScanner().getJarScanFilter(); 159 // Disable scanning for taglibs 160 jarScanFilter.setTldSkip("*"); 161 } 162 if (webapp.equals(webApps[0])) { 163 //Re-add the 1st context as also the root context 164 StandardContext rootCtx = (StandardContext) server.addWebapp("/", warfile); 165 if (taglibsScanningDisabled) { 166 StandardJarScanFilter jarScanFilter = (StandardJarScanFilter) rootCtx.getJarScanner().getJarScanFilter(); 167 // Disable scanning for taglibs 168 jarScanFilter.setTldSkip("*"); 169 } 170 } 171 } 172 catch (ServletException e) { 173 log.error("Unable to add webapp " + webapp, e); 174 } 175 } 176 } 177 178 179 /** 180 * Returns the unique instance of this class. If instance is new, starts a GUI web server. 181 * 182 * @return the instance 183 */ 184 public static synchronized GUIWebServer getInstance() { 185 if (instance == null) { 186 instance = new GUIWebServer(); 187 instance.startServer(); 188 } 189 return instance; 190 } 191 192 193 194 /** 195 * Starts a Tomcat server. 196 * 197 * @throws IOFailure if the server for any reason cannot be started. 198 */ 199 public void startServer() { 200 // start the server. 201 try { 202 server.start(); 203 204 } catch (Exception e) { 205 cleanup(); 206 log.warn("Could not start GUI", e); 207 } 208 } 209 210 /** 211 * Closes the GUI webserver, and nullifies this instance. 212 */ 213 public void cleanup() { 214 215 try { 216 server.stop(); 217 server.destroy(); 218 SiteSection.cleanup(); 219 log.info("GUI webserver has been stopped."); 220 } catch (Exception e) { 221 //throw new IOFailure("Error while stopping server", e); 222 log.warn("Error while stopping server, Trying to ignore it", e); 223 } 224 225 226 resetInstance(); 227 } 228 229 /** resetClassInstance. */ 230 private static synchronized void resetInstance() { 231 instance = null; 232 } 233}