VIVO-848 Move the FileStorage system behind an interface

Add it to the Application framework, and do some cleanup.
This commit is contained in:
Jim Blake 2014-08-14 16:54:29 -04:00
parent c751ecdc6d
commit 6e5bbaeef8
24 changed files with 232 additions and 260 deletions

View file

@ -6,12 +6,16 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener; import javax.servlet.ServletContextListener;
import edu.cornell.mannlib.vitro.webapp.filestorage.impl.FileStorageImplWrapper;
import edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessor; import edu.cornell.mannlib.vitro.webapp.imageprocessor.jai.JaiImageProcessor;
import edu.cornell.mannlib.vitro.webapp.modules.Application; import edu.cornell.mannlib.vitro.webapp.modules.Application;
import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
import edu.cornell.mannlib.vitro.webapp.searchengine.SearchEngineWrapper; import edu.cornell.mannlib.vitro.webapp.searchengine.SearchEngineWrapper;
import edu.cornell.mannlib.vitro.webapp.searchengine.solr.SolrSearchEngine; import edu.cornell.mannlib.vitro.webapp.searchengine.solr.SolrSearchEngine;
import edu.cornell.mannlib.vitro.webapp.startup.ComponentStartupStatusImpl;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
/** /**
@ -25,6 +29,7 @@ public class ApplicationImpl implements Application {
private final ServletContext ctx; private final ServletContext ctx;
private SearchEngine searchEngine; private SearchEngine searchEngine;
private ImageProcessor imageProcessor; private ImageProcessor imageProcessor;
private FileStorage fileStorage;
public ApplicationImpl(ServletContext ctx) { public ApplicationImpl(ServletContext ctx) {
this.ctx = ctx; this.ctx = ctx;
@ -44,6 +49,7 @@ public class ApplicationImpl implements Application {
this.searchEngine = searchEngine; this.searchEngine = searchEngine;
} }
@Override
public ImageProcessor getImageProcessor() { public ImageProcessor getImageProcessor() {
return imageProcessor; return imageProcessor;
} }
@ -52,11 +58,21 @@ public class ApplicationImpl implements Application {
this.imageProcessor = imageProcessor; this.imageProcessor = imageProcessor;
} }
@Override
public FileStorage getFileStorage() {
return fileStorage;
}
public void setFileStorage(FileStorage fileStorage) {
this.fileStorage = fileStorage;
}
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// The Setup class. // The Setup class.
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
public static class Setup implements ServletContextListener { public static class Setup implements ServletContextListener {
private ApplicationImpl application;
@Override @Override
public void contextInitialized(ServletContextEvent sce) { public void contextInitialized(ServletContextEvent sce) {
@ -64,14 +80,26 @@ public class ApplicationImpl implements Application {
StartupStatus ss = StartupStatus.getBean(ctx); StartupStatus ss = StartupStatus.getBean(ctx);
try { try {
ApplicationImpl application = new ApplicationImpl(ctx); application = new ApplicationImpl(ctx);
ComponentStartupStatus css = new ComponentStartupStatusImpl(
this, ss);
SearchEngine searchEngine = new SearchEngineWrapper( SearchEngine searchEngine = new SearchEngineWrapper(
new SolrSearchEngine()); new SolrSearchEngine());
searchEngine.startup(application, css);
application.setSearchEngine(searchEngine); application.setSearchEngine(searchEngine);
ss.info(this, "Started the searchEngine: " + searchEngine);
ImageProcessor imageProcessor = new JaiImageProcessor(); ImageProcessor imageProcessor = new JaiImageProcessor();
imageProcessor.startup(application, css);
application.setImageProcessor(imageProcessor); application.setImageProcessor(imageProcessor);
ss.info(this, "Started the ImageProcessor: " + searchEngine);
FileStorage fileStorage = new FileStorageImplWrapper();
fileStorage.startup(application, css);
application.setFileStorage(fileStorage);
ss.info(this, "Started the FileStorage system: " + searchEngine);
ApplicationUtils.setInstance(application); ApplicationUtils.setInstance(application);
ss.info(this, "Appliation is configured."); ss.info(this, "Appliation is configured.");
@ -82,7 +110,9 @@ public class ApplicationImpl implements Application {
@Override @Override
public void contextDestroyed(ServletContextEvent sce) { public void contextDestroyed(ServletContextEvent sce) {
// Nothing to tear down. application.getFileStorage().shutdown(application);
application.getImageProcessor().shutdown(application);
application.getSearchEngine().shutdown(application);
} }
} }

View file

@ -5,13 +5,13 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import static edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest.UNAUTHORIZED; import static edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest.UNAUTHORIZED;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItem;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.RequestedAction; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.RequestedAction;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.AddObjectPropertyStatement; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.AddObjectPropertyStatement;
@ -27,11 +27,10 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.For
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo; import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo; import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
import edu.cornell.mannlib.vitro.webapp.i18n.I18n; import edu.cornell.mannlib.vitro.webapp.i18n.I18n;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions;
import edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil; import edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil;
@ -115,23 +114,7 @@ public class ImageUploadController extends FreemarkerHttpServlet {
@Override @Override
public void init() throws ServletException { public void init() throws ServletException {
super.init(); super.init();
Object o = getServletContext().getAttribute( fileStorage = ApplicationUtils.instance().getFileStorage();
FileStorageSetup.ATTRIBUTE_NAME);
if (o instanceof FileStorage) {
fileStorage = (FileStorage) o;
} else if (o == null) {
throw new UnavailableException(this.getClass().getSimpleName()
+ " could not initialize. Attribute '"
+ FileStorageSetup.ATTRIBUTE_NAME
+ "' was not set in the servlet context.");
} else {
throw new UnavailableException(this.getClass().getSimpleName()
+ " could not initialize. Attribute '"
+ FileStorageSetup.ATTRIBUTE_NAME
+ "' in the servlet context contained an instance of '"
+ o.getClass().getName() + "' instead of '"
+ FileStorage.class.getName() + "'");
}
} }
/** /**

View file

@ -28,9 +28,9 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadControl
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.TempFileHolder; import edu.cornell.mannlib.vitro.webapp.filestorage.TempFileHolder;
import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper; import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo; import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.CropRectangle;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.Dimensions;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.ImageProcessorException; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor.ImageProcessorException;

View file

@ -11,7 +11,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
/** /**
* Static methods to help when serving uploaded files. * Static methods to help when serving uploaded files.
@ -19,6 +18,8 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
public class FileServingHelper { public class FileServingHelper {
private static final Log log = LogFactory.getLog(FileServingHelper.class); private static final Log log = LogFactory.getLog(FileServingHelper.class);
public static final String PROPERTY_DEFAULT_NAMESPACE = "Vitro.defaultNamespace";
private static final String DEFAULT_PATH = "/individual/"; private static final String DEFAULT_PATH = "/individual/";
private static final String FILE_PATH = "/file/"; private static final String FILE_PATH = "/file/";
private static boolean warned; // Only issue the warning once. private static boolean warned; // Only issue the warning once.
@ -29,11 +30,11 @@ public class FileServingHelper {
*/ */
private static String getDefaultNamespace(ServletContext ctx) { private static String getDefaultNamespace(ServletContext ctx) {
String defaultNamespace = ConfigurationProperties.getBean(ctx) String defaultNamespace = ConfigurationProperties.getBean(ctx)
.getProperty(FileStorageSetup.PROPERTY_DEFAULT_NAMESPACE); .getProperty(PROPERTY_DEFAULT_NAMESPACE);
if (defaultNamespace == null) { if (defaultNamespace == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Configuration properties must contain a value for '" "Configuration properties must contain a value for '"
+ FileStorageSetup.PROPERTY_DEFAULT_NAMESPACE + "'"); + PROPERTY_DEFAULT_NAMESPACE + "'");
} }
if (!defaultNamespace.endsWith(DEFAULT_PATH)) { if (!defaultNamespace.endsWith(DEFAULT_PATH)) {

View file

@ -4,7 +4,6 @@ package edu.cornell.mannlib.vitro.webapp.filestorage;
import java.io.IOException; import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionBindingListener;
@ -12,9 +11,9 @@ import javax.servlet.http.HttpSessionBindingListener;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage; import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo; import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
/** /**
* Attaches an uploaded file to the session with a listener, so the file will be * Attaches an uploaded file to the session with a listener, so the file will be
@ -109,6 +108,7 @@ public class TempFileHolder implements HttpSessionBindingListener {
*/ */
@Override @Override
public void valueBound(HttpSessionBindingEvent event) { public void valueBound(HttpSessionBindingEvent event) {
// Nothing to do.
} }
/** /**
@ -130,17 +130,7 @@ public class TempFileHolder implements HttpSessionBindingListener {
return; return;
} }
HttpSession session = event.getSession(); FileStorage fs = ApplicationUtils.instance().getFileStorage();
ServletContext servletContext = session.getServletContext();
FileStorage fs = (FileStorage) servletContext
.getAttribute(FileStorageSetup.ATTRIBUTE_NAME);
if (fs == null) {
log.error("Servlet context does not contain file storage at '"
+ FileStorageSetup.ATTRIBUTE_NAME + "'");
return;
}
try { try {
fs.deleteFile(fileInfo.getBytestreamUri()); fs.deleteFile(fileInfo.getBytestreamUri());
log.debug("Deleted file " + fileInfo); log.debug("Deleted file " + fileInfo);

View file

@ -22,10 +22,10 @@ import edu.cornell.mannlib.vitro.webapp.dao.InsertException;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao; import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo; import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo; import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
/** /**
* A helper object to handle the mundane details of dealing with uploaded files * A helper object to handle the mundane details of dealing with uploaded files

View file

@ -1,124 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.backend;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
/**
* Initializes the file storage system, and stores a reference in the servlet
* context.
*/
public class FileStorageSetup implements ServletContextListener {
private static final Log log = LogFactory.getLog(FileStorageSetup.class);
/**
* The implementation of the {@link FileStorage} system will be stored in
* the {@link ServletContext} as an attribute with this name.
*/
public static final String ATTRIBUTE_NAME = FileStorage.class.getName();
/**
* The default implementation will use this key to ask
* {@link ConfigurationProperties} for the vivo home directory. The file
* storage base directory is in a subdirectory below this one.
*/
public static final String PROPERTY_VITRO_HOME_DIR = "vitro.home";
public static final String FILE_STORAGE_SUBDIRECTORY = "uploads";
/**
* The default implementation will use this key to ask
* {@link ConfigurationProperties} for the default URI namespace.
*/
public static final String PROPERTY_DEFAULT_NAMESPACE = "Vitro.defaultNamespace";
/**
* Create an implementation of {@link FileStorage} and store it in the
* {@link ServletContext}, as an attribute named according to
* {@link #ATTRIBUTE_NAME}.
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
StartupStatus ss = StartupStatus.getBean(ctx);
try {
File baseDirectory = figureBaseDir(sce);
Collection<String> fileNamespace = confirmDefaultNamespace(sce);
FileStorage fs = new FileStorageImpl(baseDirectory, fileNamespace);
ctx.setAttribute(ATTRIBUTE_NAME, fs);
} catch (Exception e) {
log.fatal("Failed to initialize the file system.", e);
ss.fatal(this, "Failed to initialize the file system.", e);
}
}
/**
* Get the configuration property for the file storage base directory, and
* check that it points to an existing, writeable directory.
*
* For use by the constructor in implementations of {@link FileStorage}.
*/
private File figureBaseDir(ServletContextEvent sce) throws IOException {
String homeDirPath = ConfigurationProperties.getBean(sce).getProperty(
PROPERTY_VITRO_HOME_DIR);
if (homeDirPath == null) {
throw new IllegalArgumentException(
"Configuration properties must contain a value for '"
+ PROPERTY_VITRO_HOME_DIR + "'");
}
File homeDir = new File(homeDirPath);
if (!homeDir.exists()) {
throw new IllegalStateException("Vitro home directory '"
+ homeDir.getAbsolutePath() + "' does not exist.");
}
File baseDir = new File(homeDir, FILE_STORAGE_SUBDIRECTORY);
if (!baseDir.exists()) {
boolean created = baseDir.mkdir();
if (!created) {
throw new IOException("Unable to create uploads directory at '"
+ baseDir + "'");
}
}
return baseDir;
}
/**
* Get the configuration property for the default namespace. For use by the
* constructor in implementations of {@link FileStorage}.
*
* @returns a collection containing the default namespace.
*/
private Collection<String> confirmDefaultNamespace(ServletContextEvent sce) {
String defaultNamespace = ConfigurationProperties.getBean(sce)
.getProperty(PROPERTY_DEFAULT_NAMESPACE);
if (defaultNamespace == null) {
throw new IllegalArgumentException(
"Configuration properties must contain a value for '"
+ PROPERTY_DEFAULT_NAMESPACE + "'");
}
return Collections.singleton(defaultNamespace);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Nothing to do here.
}
}

View file

@ -1,8 +1,8 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.backend; package edu.cornell.mannlib.vitro.webapp.filestorage.impl;
import static edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage.SHORTY_LENGTH; import static edu.cornell.mannlib.vitro.webapp.filestorage.impl.FileStorageImpl.SHORTY_LENGTH;
import java.io.File; import java.io.File;
import java.util.Map; import java.util.Map;

View file

@ -1,6 +1,6 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.backend; package edu.cornell.mannlib.vitro.webapp.filestorage.impl;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -24,10 +24,29 @@ import java.util.Properties;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
/** /**
* The default implementation of {@link FileStorage}. * The default implementation of {@link FileStorage}.
*/ */
public class FileStorageImpl implements FileStorage { public class FileStorageImpl {
/**
* The name of the root directory, within the base directory.
*/
public static final String FILE_STORAGE_ROOT = "file_storage_root";
/**
* The name of the file in the base directory that holds the namespace map.
*/
public static final String FILE_STORAGE_NAMESPACES_PROPERTIES = "file_storage_namespaces.properties";
/**
* How often to we insert path separator characters?
*/
public static final int SHORTY_LENGTH = 3;
private static final Log log = LogFactory.getLog(FileStorageImpl.class); private static final Log log = LogFactory.getLog(FileStorageImpl.class);
private final File baseDir; private final File baseDir;
@ -240,7 +259,6 @@ public class FileStorageImpl implements FileStorage {
* directories to put it in. * directories to put it in.
* </p> * </p>
*/ */
@Override
public void createFile(String id, String filename, InputStream bytes) public void createFile(String id, String filename, InputStream bytes)
throws FileAlreadyExistsException, IOException { throws FileAlreadyExistsException, IOException {
String existingFilename = getFilename(id); String existingFilename = getFilename(id);
@ -291,7 +309,6 @@ public class FileStorageImpl implements FileStorage {
* will be deleted. This repeats, up to (but not including) the root * will be deleted. This repeats, up to (but not including) the root
* directory. * directory.
*/ */
@Override
public boolean deleteFile(String id) throws IOException { public boolean deleteFile(String id) throws IOException {
String existingFilename = getFilename(id); String existingFilename = getFilename(id);
if (existingFilename == null) { if (existingFilename == null) {
@ -356,14 +373,12 @@ public class FileStorageImpl implements FileStorage {
} }
/** /**
* {@inheritDoc}
* <p> * <p>
* For a non-null result, a directory must exist for the ID, and it must * For a non-null result, a directory must exist for the ID, and it must
* contain a file (it may or may not contain other directories). * contain a file (it may or may not contain other directories).
* </p> * </p>
*/ */
@Override public String getFilename(String id) {
public String getFilename(String id) throws IOException {
File dir = FileStorageHelper.getPathToIdDirectory(id, File dir = FileStorageHelper.getPathToIdDirectory(id,
this.namespacesMap, this.rootDir); this.namespacesMap, this.rootDir);
log.debug("ID '" + id + "' translates to this directory path: '" + dir log.debug("ID '" + id + "' translates to this directory path: '" + dir
@ -374,6 +389,7 @@ public class FileStorageImpl implements FileStorage {
} }
File[] files = dir.listFiles(new FileFilter() { File[] files = dir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) { public boolean accept(File pathname) {
return pathname.isFile(); return pathname.isFile();
} }
@ -395,7 +411,6 @@ public class FileStorageImpl implements FileStorage {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override
public InputStream getInputStream(String id, String filename) public InputStream getInputStream(String id, String filename)
throws IOException { throws IOException {

View file

@ -0,0 +1,122 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.impl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import javax.servlet.ServletContext;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.modules.Application;
import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
/**
* A thin wrapper around the existing FileStorageImpl. Handles the setup.
*/
public class FileStorageImplWrapper implements FileStorage {
public static final String PROPERTY_DEFAULT_NAMESPACE = "Vitro.defaultNamespace";
public static final String PROPERTY_VITRO_HOME_DIR = "vitro.home";
public static final String FILE_STORAGE_SUBDIRECTORY = "uploads";
private FileStorageImpl fs;
/**
* Create an instance of FileStorageImpl, based on the values in runtime.properties.
*/
@Override
public void startup(Application application, ComponentStartupStatus ss) {
ServletContext ctx = application.getServletContext();
try {
File baseDirectory = figureBaseDir(ctx);
Collection<String> fileNamespace = confirmDefaultNamespace(ctx);
fs = new FileStorageImpl(baseDirectory, fileNamespace);
} catch (Exception e) {
ss.fatal("Failed to initialize the file system.", e);
}
}
/**
* Get the configuration property for the file storage base directory, and
* check that it points to an existing, writeable directory.
*/
private File figureBaseDir(ServletContext ctx) throws IOException {
String homeDirPath = ConfigurationProperties.getBean(ctx).getProperty(
PROPERTY_VITRO_HOME_DIR);
if (homeDirPath == null) {
throw new IllegalArgumentException(
"Configuration properties must contain a value for '"
+ PROPERTY_VITRO_HOME_DIR + "'");
}
File homeDir = new File(homeDirPath);
if (!homeDir.exists()) {
throw new IllegalStateException("Vitro home directory '"
+ homeDir.getAbsolutePath() + "' does not exist.");
}
File baseDir = new File(homeDir, FILE_STORAGE_SUBDIRECTORY);
if (!baseDir.exists()) {
boolean created = baseDir.mkdir();
if (!created) {
throw new IOException("Unable to create uploads directory at '"
+ baseDir + "'");
}
}
return baseDir;
}
/**
* Get the configuration property for the default namespace.
*/
private Collection<String> confirmDefaultNamespace(ServletContext ctx) {
String defaultNamespace = ConfigurationProperties.getBean(ctx)
.getProperty(PROPERTY_DEFAULT_NAMESPACE);
if (defaultNamespace == null) {
throw new IllegalArgumentException(
"Configuration properties must contain a value for '"
+ PROPERTY_DEFAULT_NAMESPACE + "'");
}
return Collections.singleton(defaultNamespace);
}
@Override
public void shutdown(Application application) {
// Nothing to shut down.
}
// ----------------------------------------------------------------------
// Delegated methods
// ----------------------------------------------------------------------
@Override
public void createFile(String id, String filename, InputStream bytes)
throws FileAlreadyExistsException, IOException {
fs.createFile(id, filename, bytes);
}
@Override
public String getFilename(String id) throws IOException {
return fs.getFilename(id);
}
@Override
public InputStream getInputStream(String id, String filename)
throws FileNotFoundException, IOException {
return fs.getInputStream(id, filename);
}
@Override
public boolean deleteFile(String id) throws IOException {
return fs.deleteFile(id);
}
}

View file

@ -1,6 +1,6 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.backend; package edu.cornell.mannlib.vitro.webapp.filestorage.impl;
/** /**
* Indicates that an object ID contains an invalid character. * Indicates that an object ID contains an invalid character.

View file

@ -1,6 +1,6 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.backend; package edu.cornell.mannlib.vitro.webapp.filestorage.impl;
/** /**
* Indicates a PairTree path ("ppath" or "relative path") that is not correctly * Indicates a PairTree path ("ppath" or "relative path") that is not correctly

View file

@ -273,10 +273,10 @@ but is different in several respects:
<p> <p>
By the way, almost all of this is implemented in By the way, almost all of this is implemented in
<pre> <pre>
edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageHelper edu.cornell.mannlib.vitro.webapp.filestorage.impl.FileStorageHelper
</pre> </pre>
and illustrated in and illustrated in
<pre> <pre>
edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageHelperTest edu.cornell.mannlib.vitro.webapp.filestorage.impl.FileStorageHelperTest
</pre> </pre>
</p> </p>

View file

@ -12,19 +12,18 @@ import java.net.URLDecoder;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet; import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo; import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
/** /**
* <p> * <p>
@ -59,16 +58,8 @@ public class FileServingServlet extends VitroHttpServlet {
*/ */
@Override @Override
public void init() throws ServletException { public void init() throws ServletException {
Object o = getServletContext().getAttribute( super.init();
FileStorageSetup.ATTRIBUTE_NAME); fileStorage = ApplicationUtils.instance().getFileStorage();
if (o instanceof FileStorage) {
fileStorage = (FileStorage) o;
} else {
throw new UnavailableException(
"The ServletContext did not hold a FileStorage object at '"
+ FileStorageSetup.ATTRIBUTE_NAME
+ "'; found this instead: " + o);
}
} }
@Override @Override
@ -220,10 +211,6 @@ public class FileServingServlet extends VitroHttpServlet {
public FileServingException(String message) { public FileServingException(String message) {
super(message); super(message);
} }
public FileServingException(String message, Throwable cause) {
super(message, cause);
}
} }
} }

View file

@ -4,6 +4,7 @@ package edu.cornell.mannlib.vitro.webapp.modules;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
@ -17,6 +18,8 @@ public interface Application {
ImageProcessor getImageProcessor(); ImageProcessor getImageProcessor();
FileStorage getFileStorage();
public interface Component { public interface Component {
enum LifecycleState { enum LifecycleState {
NEW, ACTIVE, STOPPED NEW, ACTIVE, STOPPED

View file

@ -1,6 +1,6 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.backend; package edu.cornell.mannlib.vitro.webapp.modules.fileStorage;
import java.io.IOException; import java.io.IOException;

View file

@ -1,30 +1,17 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.backend; package edu.cornell.mannlib.vitro.webapp.modules.fileStorage;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import edu.cornell.mannlib.vitro.webapp.modules.Application;
/** /**
* The interface for the File Storage system. * The interface for the File Storage system.
*/ */
public interface FileStorage { public interface FileStorage extends Application.Module {
/**
* The name of the root directory, within the base directory.
*/
public static final String FILE_STORAGE_ROOT = "file_storage_root";
/**
* The name of the file in the base directory that holds the namespace map.
*/
public static final String FILE_STORAGE_NAMESPACES_PROPERTIES = "file_storage_namespaces.properties";
/**
* How often to we insert path separator characters?
*/
int SHORTY_LENGTH = 3;
/** /**
* Store the bytes from this stream as a file with the specified ID and * Store the bytes from this stream as a file with the specified ID and
* filename. If the file already exists, it is over-written. * filename. If the file already exists, it is over-written.

View file

@ -1,32 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.searchengine;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.modules.Application;
import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
import edu.cornell.mannlib.vitro.webapp.startup.ComponentStartupStatusImpl;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
/**
* Whatever search engine we have, start it up and shut it down.
*/
public class SearchEngineSetup implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
Application application = ApplicationUtils.instance();
StartupStatus ss = StartupStatus.getBean(sce.getServletContext());
ComponentStartupStatus css = new ComponentStartupStatusImpl(this, ss);
application.getSearchEngine().startup(application, css);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
Application application = ApplicationUtils.instance();
application.getSearchEngine().shutdown(application);
}
}

View file

@ -96,7 +96,7 @@ public class SearchEngineWrapper implements SearchEngine {
try { try {
throw new IllegalStateException(); throw new IllegalStateException();
} catch (Exception e) { } catch (Exception e) {
log.warn("startup called when state was " + lifecycleState, e); log.warn("shutdown called when state was " + lifecycleState, e);
} }
break; break;
} }

View file

@ -12,7 +12,6 @@ import stubs.edu.cornell.mannlib.vitro.webapp.config.ConfigurationPropertiesStub
import stubs.javax.servlet.ServletContextStub; import stubs.javax.servlet.ServletContextStub;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
/** /**
*/ */
@ -35,7 +34,7 @@ public class FileServingHelperTest extends AbstractTestClass {
ctx = new ServletContextStub(); ctx = new ServletContextStub();
ConfigurationPropertiesStub props = new ConfigurationPropertiesStub(); ConfigurationPropertiesStub props = new ConfigurationPropertiesStub();
props.setProperty(FileStorageSetup.PROPERTY_DEFAULT_NAMESPACE, props.setProperty(FileServingHelper.PROPERTY_DEFAULT_NAMESPACE,
DEFAULT_NAMESPACE); DEFAULT_NAMESPACE);
props.setBean(ctx); props.setBean(ctx);
} }

View file

@ -1,6 +1,6 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.backend; package edu.cornell.mannlib.vitro.webapp.filestorage.impl;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View file

@ -1,7 +1,9 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.backend; package edu.cornell.mannlib.vitro.webapp.filestorage.impl;
import static edu.cornell.mannlib.vitro.webapp.filestorage.impl.FileStorageImpl.FILE_STORAGE_NAMESPACES_PROPERTIES;
import static edu.cornell.mannlib.vitro.webapp.filestorage.impl.FileStorageImpl.FILE_STORAGE_ROOT;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -26,6 +28,7 @@ import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileAlreadyExistsException;
/** /**
* Test the FileStorage methods. The zero-argument constructor was tested in * Test the FileStorage methods. The zero-argument constructor was tested in
@ -55,26 +58,29 @@ public class FileStorageImplTest extends AbstractTestClass {
// tests // tests
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@SuppressWarnings("unused")
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)
public void baseDirDoesntExist() throws IOException { public void baseDirDoesntExist() throws IOException {
File baseDir = new File(tempDir, "doesntExist"); File baseDir = new File(tempDir, "doesntExist");
new FileStorageImpl(baseDir, EMPTY_NAMESPACES); new FileStorageImpl(baseDir, EMPTY_NAMESPACES);
} }
@SuppressWarnings("unused")
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void partialInitializationRoot() throws IOException { public void partialInitializationRoot() throws IOException {
File baseDir = new File(tempDir, "partialWithRoot"); File baseDir = new File(tempDir, "partialWithRoot");
baseDir.mkdir(); baseDir.mkdir();
new File(baseDir, FileStorage.FILE_STORAGE_ROOT).mkdir(); new File(baseDir, FILE_STORAGE_ROOT).mkdir();
new FileStorageImpl(baseDir, EMPTY_NAMESPACES); new FileStorageImpl(baseDir, EMPTY_NAMESPACES);
} }
@SuppressWarnings("unused")
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void partialInitializationNamespaces() throws IOException { public void partialInitializationNamespaces() throws IOException {
File baseDir = new File(tempDir, "partialWithNamespaces"); File baseDir = new File(tempDir, "partialWithNamespaces");
baseDir.mkdir(); baseDir.mkdir();
new File(baseDir, FileStorage.FILE_STORAGE_NAMESPACES_PROPERTIES) new File(baseDir, FILE_STORAGE_NAMESPACES_PROPERTIES)
.createNewFile(); .createNewFile();
new FileStorageImpl(baseDir, EMPTY_NAMESPACES); new FileStorageImpl(baseDir, EMPTY_NAMESPACES);
@ -276,7 +282,7 @@ public class FileStorageImplTest extends AbstractTestClass {
*/ */
private void assertFileContents(FileStorageImpl fs, String id, private void assertFileContents(FileStorageImpl fs, String id,
String filename, String expectedContents) throws IOException { String filename, String expectedContents) throws IOException {
File rootDir = new File(fs.getBaseDir(), FileStorage.FILE_STORAGE_ROOT); File rootDir = new File(fs.getBaseDir(), FILE_STORAGE_ROOT);
File path = FileStorageHelper.getFullPath(rootDir, id, filename, File path = FileStorageHelper.getFullPath(rootDir, id, filename,
fs.getNamespaces()); fs.getNamespaces());

View file

@ -8,6 +8,7 @@ import javax.servlet.ServletContext;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils; import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.modules.Application; import edu.cornell.mannlib.vitro.webapp.modules.Application;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
@ -66,4 +67,11 @@ public class ApplicationStub implements Application {
"ApplicationStub.getImageProcessor() not implemented."); "ApplicationStub.getImageProcessor() not implemented.");
} }
@Override
public FileStorage getFileStorage() {
throw new RuntimeException(
"ApplicationStub.getFileStorage() not implemented.");
}
} }

View file

@ -28,8 +28,6 @@ edu.cornell.mannlib.vitro.webapp.servlet.setup.rdfsetup.RDFSetup
edu.cornell.mannlib.vitro.webapp.servlet.setup.ConfigurationModelsSetup edu.cornell.mannlib.vitro.webapp.servlet.setup.ConfigurationModelsSetup
edu.cornell.mannlib.vitro.webapp.servlet.setup.ContentModelSetup edu.cornell.mannlib.vitro.webapp.servlet.setup.ContentModelSetup
edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup
edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil$Setup edu.cornell.mannlib.vitro.webapp.web.images.PlaceholderUtil$Setup
# Some permissions were removed in release 1.7 # Some permissions were removed in release 1.7
@ -65,7 +63,6 @@ edu.cornell.mannlib.vitro.webapp.i18n.selection.LocaleSelectionSetup
# The search indexer uses a "public" permission, so the PropertyRestrictionPolicyHelper # The search indexer uses a "public" permission, so the PropertyRestrictionPolicyHelper
# and the PermissionRegistry must already be set up. # and the PermissionRegistry must already be set up.
edu.cornell.mannlib.vitro.webapp.searchengine.SearchEngineSetup
edu.cornell.mannlib.vitro.webapp.searchindex.SearchIndexerSetup edu.cornell.mannlib.vitro.webapp.searchindex.SearchIndexerSetup
edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerSetup edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerSetup