/* * Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package thredds.server.cdmvalidator; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.springframework.util.StringUtils; import org.slf4j.Logger; import javax.servlet.ServletContext; import javax.servlet.RequestDispatcher; import java.io.File; import java.io.IOException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import thredds.util.filesource.*; import thredds.servlet.ThreddsConfig; import thredds.servlet.ServletUtil; import thredds.servlet.UsageLog; import thredds.server.config.TdsContext; import thredds.server.config.HtmlConfig; import ucar.nc2.util.DiskCache2; import ucar.nc2.util.DiskCache; /** * _more_ * * @author edavis * @since 4.0 */ public class CdmValidatorContext { private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger( CdmValidatorContext.class ); private String webappName; private String webappVersion; private String webappVersionBuildDate; private String contextPath; private String contentRootPath; private String contentPath; private File rootDirectory; private File contentDirectory; private FileSource configSource; private String configFileName; private HtmlConfig htmlConfig; private RequestDispatcher defaultRequestDispatcher; private RequestDispatcher jspRequestDispatcher; private DiskCache2 cdmValidateCache = null; private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 1 ); private DiskFileItemFactory fileuploadFileItemFactory; private File cacheDir; private long maxFileUploadSize; private boolean deleteImmediately = true; public CdmValidatorContext() {} public void setContentRootPath( String contentRootPath) {this.contentRootPath = contentRootPath; } public void setContentPath( String contentPath) {this.contentPath = contentPath; } public void setConfigFileName( String filename ) { this.configFileName = filename; } public String getConfigFileName() { return this.configFileName; } public void init( ServletContext servletContext ) { if ( servletContext == null ) throw new IllegalArgumentException( "ServletContext must not be null."); // ToDo LOOK - Are we still using this. ServletUtil.initDebugging( servletContext ); // Set the webapp name. this.webappName = servletContext.getServletContextName(); // Set the context path. // Servlet 2.5 allows the following. //contextPath = servletContext.getContextPath(); String tmpContextPath = servletContext.getInitParameter( "ContextPath" ); // cannot be overridden in the ThreddsConfig file if ( tmpContextPath == null ) tmpContextPath = "cdmvalidator"; contextPath = "/" + tmpContextPath; // ToDo LOOK - Get rid of need for setting contextPath in ServletUtil. ServletUtil.setContextPath( contextPath ); // Set the root directory and source. String rootPath = servletContext.getRealPath( "/" ); if ( rootPath == null ) throw new IllegalStateException( "Webapp [" + this.webappName + "] must run with exploded deployment directory (not from .war)."); this.rootDirectory = new File( rootPath ); DescendantFileSource rootDirSource = new BasicDescendantFileSource( this.rootDirectory ); this.rootDirectory = rootDirSource.getRootDirectory(); // ToDo LOOK - Get rid of need for setting rootPath in ServletUtil. ServletUtil.setRootPath( rootDirSource.getRootDirectoryPath()); // Set the content directory and source. File contentRootDir = new File( this.contentRootPath ); if ( ! contentRootDir.isAbsolute() ) this.contentDirectory = new File( new File( this.rootDirectory, this.contentRootPath ), this.contentPath ); else { if ( contentRootDir.isDirectory() ) this.contentDirectory = new File( contentRootDir, this.contentPath ); else throw new IllegalStateException( "Content root directory [" + this.contentRootPath + "] not a directory."); } // If the content directory doesn't exist, try to create it. if ( ! this.contentDirectory.exists() ) { if ( ! this.contentDirectory.mkdirs()) { String tmpMsg = "Content directory does not exist and could not be created"; log.error( "init(): " + tmpMsg + " [" + this.contentDirectory.getAbsolutePath() + "]" ); throw new IllegalStateException( tmpMsg ); } } // Make sure content directory is a directory. DescendantFileSource contentDirSource; if ( this.contentDirectory.isDirectory() ) { contentDirSource = new BasicDescendantFileSource( StringUtils.cleanPath( this.contentDirectory.getAbsolutePath() ) ); this.contentDirectory = contentDirSource.getRootDirectory(); } else { String tmpMsg = "Content directory not a directory"; log.error( "init(): " + tmpMsg + " [" + this.contentDirectory.getAbsolutePath() + "]" ); throw new IllegalStateException( tmpMsg ); } ServletUtil.setContentPath( contentDirSource.getRootDirectoryPath()); // If config file doesn't exist, try to create. File configFile = contentDirSource.getFile( this.configFileName ); if ( configFile == null ) { // Get target File. configFile = new File( this.contentDirectory, this.configFileName ); // Find template configuration file. // ToDo LOOK - Move WEB-INF/altContent path to cdmvalidator.properties.TEMPLAT file. String templateConfigFilePath = "WEB-INF/altContent/" + this.configFileName; File templateConfigFile = rootDirSource.getFile( templateConfigFilePath ); if ( templateConfigFile == null ) { String tmpMsg = "Non-existent template configuration file"; log.error( "init(): " + tmpMsg + " [" + templateConfigFilePath + "]" ); throw new IllegalStateException( tmpMsg ); } try { ucar.nc2.util.IO.copyFile( templateConfigFile, configFile ); } catch ( IOException e ) { String tmpMsg = "Configuration file doesn't exist and could not be created"; log.error( "init(): " + tmpMsg + " [" + configFile.getAbsolutePath() + "]", e ); throw new IllegalStateException( tmpMsg, e ); } } // read in persistent user-defined params from threddsConfig.xml ThreddsConfig.init( configFile.getPath() ); this.configSource = contentDirSource; jspRequestDispatcher = servletContext.getNamedDispatcher( "jsp" ); defaultRequestDispatcher = servletContext.getNamedDispatcher( "default" ); if ( this.htmlConfig != null ) this.htmlConfig.init( this.getWebappName(), this.getWebappVersion(), this.getWebappVersionBuildDate(), this.getWebappContextPath() ); this.initCaching(); } private void initCaching() { // Configure CdmValidator: max upload size; cache dir and scheme. maxFileUploadSize = ThreddsConfig.getBytes( "CdmValidatorService.maxFileUploadSize", (long) 1000 * 1000 * 1000 ); String cacheDirPath = ThreddsConfig.get( "CdmValidatorService.cache.dir", new File( this.getContentDirectory(), "/cache/cdmValidate" ).getPath() ); int scourSecs = ThreddsConfig.getSeconds( "CdmValidatorService.cache.scour", -1 ); int maxAgeSecs = ThreddsConfig.getSeconds( "CdmValidatorService.cache.maxAge", -1 ); final long maxSize = ThreddsConfig.getBytes( "CdmValidatorService.cache.maxSize", (long) 1000 * 1000 * 1000 ); // 1 Gbyte if ( maxAgeSecs > 0 ) { // Setup cache used by CDM stack (uses DiskCache which is an older static disk cache impl). DiskCache.setRootDirectory( cacheDirPath ); DiskCache.setCachePolicy( true ); if ( !scheduler.isShutdown() ) { Runnable command = new Runnable() { public void run() { StringBuilder sb = new StringBuilder(); DiskCache.cleanCache( maxSize, sb ); // 1 Gbyte sb.append( "----------------------\n" ); log.debug( "init():Runnable:run(): Scour on ucar.nc2.util.DiskCache:\n" + sb ); } }; scheduler.scheduleAtFixedRate( command, scourSecs / 2, scourSecs, TimeUnit.SECONDS ); } // Setup cache for file upload (uses DiskCache2 which is a newer disk cache impl). deleteImmediately = false; cdmValidateCache = new DiskCache2( cacheDirPath, false, maxAgeSecs / 60, scourSecs / 60 ); } // Setup file upload factory. cacheDir = new File( cacheDirPath ); if ( !cacheDir.exists() && !cacheDir.mkdirs() ) { String msg = "File upload cache directory [" + cacheDir + "] doesn't exist and couldn't be created."; log.error( "init(): " + msg + " - " + UsageLog.closingMessageNonRequestContext() ); throw new IllegalStateException( msg ); } fileuploadFileItemFactory = new DiskFileItemFactory( 0, cacheDir ); // LOOK can also do in-memory } public void destroy() { if ( this.cdmValidateCache != null ) this.cdmValidateCache.exit(); if ( this.scheduler != null ) this.scheduler.shutdown(); } /** * Return the name of the webapp as given by the display-name element in web.xml. * * @return the name of the webapp as given by the display-name element in web.xml. */ public String getWebappName() { return this.webappName; } /** * Return the context path under which this web app is running (e.g., "/thredds"). * * @return the context path. */ public String getWebappContextPath() { return contextPath; } /** * Return the full version string (<major>.<minor>.<bug>.<build>) * for this web application. * * @return the full version string. */ public String getWebappVersion() { return this.webappVersion; } public void setWebappVersion( String verFull ) { this.webappVersion = verFull; } public String getWebappVersionBuildDate() { return this.webappVersionBuildDate; } public void setWebappVersionBuildDate( String buildDateString) { this.webappVersionBuildDate = buildDateString; } public void setHtmlConfig( HtmlConfig htmlConfig ) { this.htmlConfig = htmlConfig; } /** * Return the HtmlConfig object for this context. * * @return the HtmlConfig */ public HtmlConfig getHtmlConfig() { return this.htmlConfig; } /** * Return the web apps root directory (i.e., getRealPath( "/")). * * @return the root directory for the web app. */ public File getRootDirectory() { return rootDirectory; } /** * Return File for content directory (exists() may be false). * * @return a File to the content directory. */ public File getContentDirectory() { return contentDirectory; } public FileSource getConfigFileSource() { return this.configSource; } public RequestDispatcher getDefaultRequestDispatcher() { return this.defaultRequestDispatcher; } public RequestDispatcher getJspRequestDispatcher() { return this.jspRequestDispatcher; } public static Logger getLog() { return log; } public File getCacheDir() { return cacheDir; } public long getMaxFileUploadSize() { return maxFileUploadSize; } public boolean isDeleteImmediately() { return deleteImmediately; } public FileItemFactory getFileuploadFileItemFactory() { return this.fileuploadFileItemFactory; } }