NIHVIVO-3524 UpdateUploadedFiles (and dependent classes) was used to migrate from 0.9 to 1.0 and from 1.1.1 to 1.2 - it is no longer needed.

This commit is contained in:
j2blake 2012-03-19 16:05:21 +00:00
parent acf1b1896a
commit 04eb46415a
16 changed files with 0 additions and 2014 deletions

View file

@ -1,64 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import java.io.IOException;
import com.hp.hpl.jena.rdf.model.Resource;
/**
* Adjust any individual that has a thumbnail with no main image.
*/
public class AllThumbsAdjuster extends FsuScanner {
private ImageDirectoryWithBackup imageDirectoryWithBackup;
public AllThumbsAdjuster(FSUController controller) {
super(controller);
this.imageDirectoryWithBackup = controller
.getImageDirectoryWithBackup();
}
/**
* For every individual with thumbnails but no main images, create a main
* image from the first thumbnail.
*/
public void adjust() {
updateLog.section("Creating main images for thumbnails "
+ "that have none.");
for (Resource resource : ModelWrapper.listResourcesWithProperty(model,
thumbProperty)) {
if (ResourceWrapper.getProperty(resource, imageProperty) == null) {
createMainImageFromThumbnail(resource);
}
}
}
/**
* This individual has a thumbnail but no main image. Create one.
* <ul>
* <li>Figure a name for the main image.</li>
* <li>Copy the thumbnail image file into the main image file.</li>
* <li>Set that file as an image (old-style) on the individual.</li>
* </ul>
*/
private void createMainImageFromThumbnail(Resource resource) {
String thumbFilename = getValues(resource, thumbProperty).get(0);
String mainFilename = addFilenamePrefix("_main_image_", thumbFilename);
updateLog.log(resource, "creating a main file at '" + mainFilename
+ "' to match the thumbnail at '" + thumbFilename + "'");
try {
File thumbFile = imageDirectoryWithBackup
.getExistingFile(thumbFilename);
File mainFile = imageDirectoryWithBackup.getNewfile(mainFilename);
mainFile = checkNameConflicts(mainFile);
FileUtil.copyFile(thumbFile, mainFile);
ResourceWrapper.addProperty(resource, imageProperty, mainFilename);
} catch (IOException e) {
updateLog.error(resource, "failed to create main file '"
+ mainFilename + "'", e);
}
}
}

View file

@ -1,71 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
/**
* Removes any image properties (main or thumbnail) that point to files that
* don't actually exist.
*/
public class DeadEndPropertyRemover extends FsuScanner {
private ImageDirectoryWithBackup imageDirectoryWithBackup;
public DeadEndPropertyRemover(FSUController controller) {
super(controller);
this.imageDirectoryWithBackup = controller
.getImageDirectoryWithBackup();
}
/**
* Remove dead end properties for both main images and thumbnails.
*/
public void remove() {
updateLog.section("Removing image properties whose "
+ "referenced files do not exist.");
removeDeadEndProperties(imageProperty, "main image");
removeDeadEndProperties(thumbProperty, "thumbnail");
}
/**
* Check all of the individuals that possess this property.
*/
private void removeDeadEndProperties(Property prop, String label) {
for (Resource resource : ModelWrapper.listResourcesWithProperty(model,
prop)) {
removeDeadEndPropertiesFromResource(resource, prop, label);
}
}
/**
* Check these statments on this resource. If any of them does not point to
* an existing file, remove the statement.
*/
private void removeDeadEndPropertiesFromResource(Resource resource,
Property prop, String label) {
for (Statement stmt : getStatements(resource, prop)) {
RDFNode node = stmt.getObject();
if (node.isLiteral()) {
String filename = ((Literal) node).getString();
File file = imageDirectoryWithBackup.getExistingFile(filename);
if (!file.exists()) {
updateLog.warn(
resource,
"removing link to " + label + " '" + filename
+ "': file does not exist at '"
+ file.getAbsolutePath() + "'.");
ModelWrapper.removeStatement(model, stmt);
}
}
}
}
}

View file

@ -1,38 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import com.hp.hpl.jena.rdf.model.Model;
import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
/**
* An object that can initialize one of the {@link FsuScanner}s.
*/
public interface FSUController {
/** The Jena model. */
Model getModel();
/** The update log. */
FSULog getUpdateLog();
/** The place to find or to create image files. */
ImageDirectoryWithBackup getImageDirectoryWithBackup();
/** A helper with access to the DAO layer and the file storage system. */
UploadedFileHelper getUploadedFileHelper();
/** The file storage system. */
FileStorage getFileStorage();
/** Where to store the files that were translated. */
File getTranslatedDirectory();
/** Where to store the files that weren't in use anyway. */
File getUnreferencedDirectory();
}

View file

@ -1,181 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
/**
* Writes the log file for the {@link FileStorageUpdater}. Be sure to call
* {@link #close()} when finished.
*/
public class FSULog {
private final SimpleDateFormat timeStamper = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
private final File logFile;
private final PrintWriter writer;
private boolean open;
FSULog(File logDirectory, String prefix) throws IOException {
this.logFile = generateTimestampedFilename(logDirectory, prefix);
this.writer = new PrintWriter(this.logFile);
open = true;
}
/**
* Create a filename for the log file that contains a timestamp, so if we
* run the process more than once, we will see multiple files.
*/
private File generateTimestampedFilename(File upgradeDirectory,
String prefix) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-sss");
String filename = prefix + "." + sdf.format(new Date()) + ".txt";
return new File(upgradeDirectory, filename);
}
/**
* Where are we writing the output?
*/
String getFilename() {
return this.logFile.getAbsolutePath();
}
/**
* Write this message.
*/
void log(String message) {
writer.println(timeStamper.format(new Date()) + " INFO " + message);
}
/**
* Write this message about this resource.
*/
void log(Resource resource, String message) {
log(showResource(resource) + message);
}
/**
* Write this warning message.
*/
public void warn(String message) {
writer.println(timeStamper.format(new Date()) + " WARN " + message);
}
/**
* Write this warning message about this resource.
*/
public void warn(Resource resource, String message) {
warn(showResource(resource) + message);
}
/**
* Write this error message.
*/
void error(String message) {
writer.println(timeStamper.format(new Date()) + " ERROR " + message);
}
/**
* Write this exception as an error message..
*/
void error(Exception e) {
error(e.toString());
e.printStackTrace(writer);
}
/**
* Write an error message with this exception.
*/
public void error(String message, Exception e) {
error(message);
e.printStackTrace(writer);
}
/**
* Write an error message about this resource and with this exception.
*/
public void error(Resource resource, String message) {
error(showResource(resource) + message);
}
/**
* Write an error message about this resource and with this exception.
*/
public void error(Resource resource, String message, Exception e) {
error(showResource(resource) + message, e);
}
/**
* Write a section heading.
*/
public void section(String message) {
log(">>>>>>>>>> ");
log(">>>>>>>>>> " + message);
log(">>>>>>>>>> ");
}
/**
* Close the writer, if not already closed.
*/
public void close() {
if (open) {
writer.close();
open = false;
}
}
/**
* Format the resource label and URI for output in a message.
*/
private String showResource(Resource resource) {
return "On resource '" + getLabel(resource) + "' (" + getUri(resource)
+ "), ";
}
/**
* Find the URI for this resource, if there is one.
*/
private String getUri(Resource resource) {
if (resource != null) {
String uri = resource.getURI();
if (uri != null) {
return uri;
}
}
return "no URI";
}
/**
* Find the label for this resource, if there is one.
*/
private String getLabel(Resource resource) {
if (resource != null) {
Model model = resource.getModel();
if (model != null) {
Property prop = model.createProperty(VitroVocabulary.LABEL);
Statement stmt = resource.getProperty(prop);
if (stmt != null) {
RDFNode node = stmt.getObject();
if (node.isLiteral()) {
return ((Literal) node).getString();
}
}
}
}
return "no label";
}
}

View file

@ -1,352 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
/**
* Check all of the FileByteStream objects. If there are any that don't have
* alias URLs, fix them.
*/
public class FileStorageAliasAdder {
private static final Log log = LogFactory
.getLog(FileStorageAliasAdder.class);
private static final String FILE_PATH = "/file/";
/**
* Query: get all bytestream resources that do not have alias URLs.
*/
private static final String QUERY_BYTESTREAMS_WITHOUT_ALIASES = ""
+ "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
+ "PREFIX public: <http://vitro.mannlib.cornell.edu/ns/vitro/public#>\n"
+ "SELECT ?bs\n" + "WHERE {\n"
+ " ?bs rdf:type public:FileByteStream\n"
+ " OPTIONAL { ?bs public:directDownloadUrl ?alias }\n"
+ " FILTER ( !BOUND(?alias) )\n" + "}\n";
/**
* Query: get the filenames for all bytestream resources that do not have
* alias URLs.
*/
private static final String QUERY_FILENAMES_FOR_BYTESTREAMS = ""
+ "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
+ "PREFIX public: <http://vitro.mannlib.cornell.edu/ns/vitro/public#>\n"
+ "SELECT ?bs ?fn\n" + "WHERE {\n"
+ " ?bs rdf:type public:FileByteStream . \n"
+ " ?f public:downloadLocation ?bs . \n"
+ " ?f public:filename ?fn . \n"
+ " OPTIONAL { ?bs public:directDownloadUrl ?alias . }\n"
+ " FILTER ( !BOUND(?alias) )\n" + "}\n";
private final Model model;
private final File upgradeDirectory;
private final String vivoDefaultNamespace;
private FSULog updateLog;
private Set<String> bytestreamUrisWithoutAliases;
private Map<String, String> bytestreamUrisAndFilenames;
public FileStorageAliasAdder(Model model, File uploadDirectory,
String vivoDefaultNamespace) {
this.model = model;
this.upgradeDirectory = new File(uploadDirectory, "upgrade");
this.vivoDefaultNamespace = vivoDefaultNamespace;
}
/**
* Go through all of the FileByteStream objects in the model, creating Alias
* URLs for any objects that don't have them.
*
* If there is nothing to do, don't even create a log file, just exit.
*
* If there is something to do, go through the whole process.
*
* At the end, there should be nothing to do.
*/
public void update() {
// If there is nothing to do, we're done: don't even create a log file.
if (!isThereAnythingToDo()) {
log.debug("Found no FileByteStreams without alias URLs.");
return;
}
setup();
try {
findAndAddMissingAliasUrls();
if (isThereAnythingToDo()) {
throw new IllegalStateException("FileStorageAliasAdder "
+ "was unsuccessful -- model still contains "
+ "FileByteStreams without alias URLs.");
}
updateLog.section("Finished adding alias URLs to FileByteStreams.");
} finally {
updateLog.close();
}
log.info("Finished adding alias URLs to FileByteStreams.");
}
/**
* Query the model. If there are any FileByteStream objects with no Alias
* URL, we have work to do.
*/
private boolean isThereAnythingToDo() {
String queryString = QUERY_BYTESTREAMS_WITHOUT_ALIASES;
log.debug("query: " + queryString);
QueryExecution qexec = null;
try {
qexec = createQueryExecutor(queryString);
ResultSet results = qexec.execSelect();
boolean foundSome = results.hasNext();
log.debug("any work to do? " + foundSome);
return foundSome;
} catch (Exception e) {
log.error(e, e);
return false;
} finally {
if (qexec != null) {
qexec.close();
}
}
}
/**
* Create the upgrade directory. Create the log file. If we fail, drop dead.
*/
private void setup() {
try {
this.upgradeDirectory.mkdirs();
updateLog = new FSULog(this.upgradeDirectory,
"FileStorageAliasAdder-log");
log.info("Updating pre-1.1 file references. Log file is "
+ updateLog.getFilename());
} catch (IOException e) {
if (updateLog != null) {
updateLog.close();
}
throw new IllegalStateException("can't create log file: '"
+ updateLog.getFilename() + "'", e);
}
}
/**
* Add an alias URL to any FileByteStream object that doesn't have one.
*/
private void findAndAddMissingAliasUrls() {
findBytestreamsWithoutAliasUrls();
findFilenamesForBytestreams();
addAliasUrlsToModel();
}
/**
* Find every bytestream that doesn't have an alias URL.
*/
private void findBytestreamsWithoutAliasUrls() {
BytestreamUriUnpacker unpacker = new BytestreamUriUnpacker();
runQuery(QUERY_BYTESTREAMS_WITHOUT_ALIASES, unpacker);
this.bytestreamUrisWithoutAliases = unpacker.getUris();
log.debug("Found " + unpacker.getUris().size()
+ " bytestreams without alias URLs");
}
/**
* Find the filename for every bytestream that doesn't have an alias URL.
*/
private void findFilenamesForBytestreams() {
FilenameUnpacker unpacker = new FilenameUnpacker();
runQuery(QUERY_FILENAMES_FOR_BYTESTREAMS, unpacker);
this.bytestreamUrisAndFilenames = unpacker.getFilenameMap();
log.debug("Found " + unpacker.getFilenameMap().size()
+ " bytestreams with filenames but no alias URLs");
}
/** Add an alias URL to each resource in the list. */
private void addAliasUrlsToModel() {
if (this.bytestreamUrisWithoutAliases.isEmpty()) {
updateLog.warn("Found no bytestreams without aliases. "
+ "Why am I here?");
return;
}
Property aliasProperty = model
.createProperty(VitroVocabulary.FS_ALIAS_URL);
for (String bytestreamUri : this.bytestreamUrisWithoutAliases) {
String aliasUrl = figureAliasUrl(bytestreamUri);
Resource resource = model.getResource(bytestreamUri);
ModelWrapper.add(model, resource, aliasProperty, aliasUrl);
updateLog.log(resource, "added alias URL: '" + aliasUrl + "'");
}
}
/**
* Convert the bytestream URI and the filename into an alias URL.
*
* If they aren't in our default namespace, or they don't have a filename,
* then their URI is the best we can do for an alias URL.
*/
private String figureAliasUrl(String bytestreamUri) {
if (!bytestreamUri.startsWith(vivoDefaultNamespace)) {
updateLog.warn("bytestream uri does not start "
+ "with the default namespace: '" + bytestreamUri + "'");
return bytestreamUri;
}
String filename = this.bytestreamUrisAndFilenames.get(bytestreamUri);
if (filename == null) {
updateLog.warn("bytestream has no surrogate or no filename: '"
+ bytestreamUri + "'");
return "filename_not_found";
}
try {
String remainder = bytestreamUri.substring(vivoDefaultNamespace
.length());
String encodedFilename = URLEncoder.encode(filename, "UTF-8");
String separator = remainder.endsWith("/") ? "" : "/";
return FILE_PATH + remainder + separator + encodedFilename;
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e); // No UTF-8? Can't happen.
}
}
private void runQuery(String queryString, QueryResultUnpacker unpacker) {
log.debug("query: " + queryString);
QueryExecution qexec = null;
try {
qexec = createQueryExecutor(queryString);
Iterator<QuerySolution> results = qexec.execSelect();
while (results.hasNext()) {
QuerySolution result = results.next();
if (log.isDebugEnabled()) {
log.debug("Query result variables: "
+ listVariables(result));
}
unpacker.unpack(result);
}
} catch (Exception e) {
log.error(e, e);
} finally {
if (qexec != null) {
qexec.close();
}
}
}
private QueryExecution createQueryExecutor(String queryString) {
Query query = QueryFactory.create(queryString);
return QueryExecutionFactory.create(query, model);
}
/** For debug logging. */
private List<String> listVariables(QuerySolution result) {
List<String> list = new ArrayList<String>();
for (Iterator<String> names = result.varNames(); names.hasNext();) {
String name = names.next();
RDFNode value = result.get(name);
list.add(name + "=" + value);
}
return list;
}
// ----------------------------------------------------------------------
// Helper classes
// ----------------------------------------------------------------------
private interface QueryResultUnpacker {
public abstract void unpack(QuerySolution result);
}
private class BytestreamUriUnpacker implements QueryResultUnpacker {
private final Set<String> uris = new HashSet<String>();
@Override
public void unpack(QuerySolution result) {
Resource bytestream = result.getResource("bs");
if (bytestream == null) {
updateLog.error("Query result contains no "
+ "bytestream resource: " + result);
return;
}
uris.add(bytestream.getURI());
}
public Set<String> getUris() {
return uris;
}
}
private class FilenameUnpacker implements QueryResultUnpacker {
private final Map<String, String> filenameMap = new HashMap<String, String>();
@Override
public void unpack(QuerySolution result) {
Resource bytestream = result.getResource("bs");
if (bytestream == null) {
updateLog.error("Query result contains no "
+ "bytestream resource: " + result);
return;
}
String bytestreamUri = bytestream.getURI();
Literal filenameLiteral = result.getLiteral("fn");
if (filenameLiteral == null) {
updateLog.error("Query result for '" + bytestreamUri
+ "' contains no filename.");
return;
}
String filename = filenameLiteral.getString();
filenameMap.put(bytestreamUri, filename);
}
public Map<String, String> getFilenameMap() {
return filenameMap;
}
}
}

View file

@ -1,268 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import javax.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.rdf.model.Model;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
/**
* <p>
* Clean up any files that are stored in the old directory structure and
* referenced by old-style image properties.
* </p>
* <p>
* Besides converting the files to the new framework, this process will produce
* these artifacts:
* <ul>
* <li>A log file in the uploaded files directory, with a timestamped name, such
* as <code>upgrade/upgradeLog2010-06-20T14-55-00.txt</code>, for example. If
* for any reason, the upgrade process must run again, the log will not be
* overwritten.</li>
* <li>A directory of "deleted" files - these were extra thumbnail files or
* extra main image files -- see the details below.</li>
* <li>A directory of "unreferenced" files - these were in the image directory,
* but not connected to any entity.</li>
* </ul>
* </p>
* <p>
* We consider some special cases:
* <ul>
* <li>An individual may refer to an image file that does not actually exist. If
* so, that reference will be deleted.</li>
* <li>There may be more than one reference to the same image file. If so, all
* but the first such reference will be deleted.</li>
* <li>
* In the old style, it was possible to have a main image without a thumbnail.
* If we find that, we will generate a scaled down copy of the main image, store
* that as a thumbnail image file, and then proceed.</li>
* <li>
* In the old style, it was possible to have a thumbnail without a main image.
* If we find that, we will make a copy of the thumbnail image file, declare
* that copy to be the main image, and then proceed.</li>
* <li>
* We may find individuals with more than one main image, or more than one
* thumbnail. If so, we will discard all but the first one (move them to the
* "deleted" directory).</li>
* </ul>
* </p>
* <p>
* Aside from these special cases, we will:
* <ul>
* <li>Translate the main image.
* <ul>
* <li>Store the image in the new file system.</li>
* <li>Delete the image from the old images directory.</li>
* <li>Create a ByteStream individual for the main image.</li>
* <li>Create a Surrogate individual for the main image.</li>
* <li>Tie these together and attach to the entity that owns the image.</li>
* </ul>
* </li>
* <li>Translate the thumbnail.
* <ul>
* <li>Store the thumbnail in the new file system.</li>
* <li>Create a ByteStream individual for the thumbnail.</li>
* <li>Create a Surrogate individual for the thumbnail.</li>
* <li>Tie these together and attach to the Surrogate for the main image.</li>
* </ul>
* </li>
* </ul>
* </p>
* <p>
* After processing all of these cases, there may be some images remaining in
* the "images" directory. These will be moved to the "unreferenced" directory,
* while preserving any internal tree structure.
* </p>
*/
public class FileStorageUpdater implements FSUController {
private static final Log log = LogFactory.getLog(FileStorageUpdater.class);
/** How wide should a generated thumbnail image be (in pixels)? */
public static final int THUMBNAIL_WIDTH = 200;
/** How high should a generated thumbnail image be (in pixels)? */
public static final int THUMBNAIL_HEIGHT = 200;
/** How is the main image referenced in the old scheme? */
public static final String IMAGEFILE = VitroVocabulary.vitroURI
+ "imageFile";
/** How is the thumbnail referenced in the old scheme? */
public static final String IMAGETHUMB = VitroVocabulary.vitroURI
+ "imageThumb";
private final Model model;
private final FileStorage fileStorage;
private final UploadedFileHelper uploadedFileHelper;
private final ImageDirectoryWithBackup imageDirectoryWithBackup;
private final File upgradeDirectory;
private FSULog updateLog;
public FileStorageUpdater(WebappDaoFactory wadf, Model model,
FileStorage fileStorage, File uploadDirectory,
File webappImageDirectory, ServletContext ctx) {
this.model = model;
this.fileStorage = fileStorage;
this.uploadedFileHelper = new UploadedFileHelper(fileStorage, wadf, ctx);
this.upgradeDirectory = new File(uploadDirectory, "upgrade");
this.imageDirectoryWithBackup = new ImageDirectoryWithBackup(new File(
uploadDirectory, "images"), webappImageDirectory);
}
/**
* <p>
* Go through all of the individuals who have image files or thumbnail
* files, adjusting them to the new way.
* </p>
* <p>
* If there is nothing to do, don't even create a log file, just exit.
* </p>
* <p>
* If there is something to do, go through the whole process.
* </p>
* <p>
* At the end, there should be nothing to do. If that's true, clean out the
* old images directory.
* </p>
*/
public void update() {
// If there is nothing to do, we're done: don't even create a log file.
if (!isThereAnythingToDo()) {
log.debug("Found no pre-1.1 file references.");
return;
}
// Create the upgrade directory and the log file.
setup();
try {
// Remove any image properties that don't point to literals.
new NonLiteralPropertyRemover(this).remove();
// Remove any image properties that point to files that don't exist.
new DeadEndPropertyRemover(this).remove();
// No resource may have multiple main images or multiple thumbnails.
new MultiplePropertyRemover(this).remove();
// Create a main image for any thumbnail that doesn't have one.
new AllThumbsAdjuster(this).adjust();
// Create a thumbnail for any main image that doesn't have one.
new NoThumbsAdjuster(this).adjust();
// Copy all images into the new file storage system, translating
// into the new schema. Get a list of all the images we translated.
ImageSchemaTranslater translater = new ImageSchemaTranslater(this);
Collection<String> translatedFiles = translater.translate();
if (isThereAnythingToDo()) {
throw new IllegalStateException(
"FileStorageUpdate was unsuccessful -- "
+ "model still contains pre-1.1 file references.");
}
// Clean out the old image directory, separating into files which
// were translated, and files for which we found no reference.
new ImageDirectoryCleaner(this).clean(translatedFiles);
updateLog.section("File Storage update is complete.");
} finally {
updateLog.close();
}
log.info("Finished updating pre-1.1 file references.");
}
/**
* Query the model. If there are any resources with old-style image
* properties, we have work to do.
*/
private boolean isThereAnythingToDo() {
if (!ModelWrapper.listResourcesWithProperty(model,
model.createProperty(IMAGEFILE)).isEmpty()) {
return true;
}
if (!ModelWrapper.listResourcesWithProperty(model,
model.createProperty(IMAGETHUMB)).isEmpty()) {
return true;
}
return false;
}
/**
* Create the upgrade directory. Create the log file. If we fail, drop dead.
*/
private void setup() {
try {
this.upgradeDirectory.mkdirs();
updateLog = new FSULog(this.upgradeDirectory,
"FileStorageUpdater-log");
log.info("Updating pre-1.1 file references. Log file is "
+ updateLog.getFilename());
} catch (IOException e) {
if (updateLog != null) {
updateLog.close();
}
throw new IllegalStateException("can't create log file: '"
+ updateLog.getFilename() + "'", e);
}
}
// ----------------------------------------------------------------------
// Methods to set up the individual scanners.
// ----------------------------------------------------------------------
@Override
public Model getModel() {
return this.model;
}
@Override
public FSULog getUpdateLog() {
return this.updateLog;
}
@Override
public UploadedFileHelper getUploadedFileHelper() {
return this.uploadedFileHelper;
}
@Override
public FileStorage getFileStorage() {
return this.fileStorage;
}
@Override
public ImageDirectoryWithBackup getImageDirectoryWithBackup() {
return this.imageDirectoryWithBackup;
}
@Override
public File getTranslatedDirectory() {
return new File(this.upgradeDirectory, "translatedImages");
}
@Override
public File getUnreferencedDirectory() {
return new File(this.upgradeDirectory, "unreferencedImages");
}
}

View file

@ -1,107 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* A collection of static routines for moving, copying and deleting files.
*/
public class FileUtil {
/**
* Copy a file from one location to another, and remove it from the original
* location.
*/
public static void moveFile(File from, File to) throws IOException {
copyFile(from, to);
deleteFile(from);
}
/**
* Copy a file from one location to another.
*/
public static void copyFile(File from, File to) throws IOException {
if (!from.exists()) {
throw new FileNotFoundException("File '" + from.getAbsolutePath()
+ "' does not exist.");
}
InputStream in = null;
try {
in = new FileInputStream(from);
writeFile(in, to);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Create a file with the contents of this data stream.
*
* @param stream
* the data stream. You must close it afterward.
*/
public static void writeFile(InputStream stream, File to)
throws IOException {
if (to.exists()) {
throw new IOException("File '" + to.getAbsolutePath()
+ "' already exists.");
}
File parent = to.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
if (!parent.exists()) {
throw new IOException("Can't create parent directory for '"
+ to.getAbsolutePath() + "'");
}
}
OutputStream out = null;
try {
out = new FileOutputStream(to);
byte[] buffer = new byte[8192];
int howMany;
while (-1 != (howMany = stream.read(buffer))) {
out.write(buffer, 0, howMany);
}
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Delete this file, and make sure that it's gone.
*/
public static void deleteFile(File file) throws IOException {
file.delete();
if (file.exists()) {
throw new IOException("Failed to delete file '"
+ file.getAbsolutePath() + "'");
}
}
/** No need to instantiate it -- all methods are static. */
private FileUtil() {
// Nothing to instantiate.
}
}

View file

@ -1,126 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.shared.Lock;
/**
* Base class for the tools that scan the model. Holds some useful fields and
* some utility methods.
*/
public abstract class FsuScanner {
protected final Model model;
protected final FSULog updateLog;
protected final Property imageProperty;
protected final Property thumbProperty;
public FsuScanner(FSUController controller) {
this.model = controller.getModel();
this.updateLog = controller.getUpdateLog();
this.imageProperty = model.createProperty(FileStorageUpdater.IMAGEFILE);
this.thumbProperty = model
.createProperty(FileStorageUpdater.IMAGETHUMB);
}
/**
* Read all of the specified properties on a resource, and return a
* {@link List} of the {@link String} values.
*/
protected List<String> getValues(Resource resource, Property property) {
List<String> list = new ArrayList<String>();
StmtIterator stmts = resource.listProperties(property);
try {
while (stmts.hasNext()) {
Statement stmt = stmts.next();
RDFNode object = stmt.getObject();
if (object.isLiteral()) {
list.add(((Literal) object).getString());
} else {
updateLog.error(resource,
"property value was not a literal: "
+ "property is '" + property.getURI()
+ "', value is '" + object + "'");
}
}
} finally {
stmts.close();
}
return list;
}
/**
* Read all of the specified properties on a resource, and return a
* {@link List} of the {@link Statement}s.
*/
protected List<Statement> getStatements(Resource resource, Property property) {
List<Statement> list = new ArrayList<Statement>();
resource.getModel().enterCriticalSection(Lock.READ);
StmtIterator stmts = resource.listProperties(property);
try {
while (stmts.hasNext()) {
list.add(stmts.next());
}
} finally {
stmts.close();
resource.getModel().leaveCriticalSection();
}
return list;
}
/**
* Find the filename within a path so we can add this prefix to it, while
* retaining the path.
*/
protected String addFilenamePrefix(String prefix, String path) {
int slashHere = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
if (slashHere == -1) {
return prefix + path;
} else {
String dirs = path.substring(0, slashHere + 1);
String filename = path.substring(slashHere + 1);
return dirs + prefix + filename;
}
}
/**
* We are about to create a file - if a file of this name already exists,
* increment the name until we have no collision.
*
* @return the original file, or the file with the incremented name.
*/
protected File checkNameConflicts(final File file) {
if (!file.exists()) {
// No conflict.
return file;
}
File parent = file.getParentFile();
String filename = file.getName();
for (int i = 0; i < 100; i++) {
File newFile = new File(parent, i + filename);
if (!newFile.exists()) {
updateLog.log("File '" + file + "' already exists, using '"
+ newFile + "' to avoid conflict.");
return newFile;
}
}
updateLog.error("File '" + file
+ "' already exists. Unable to avoid conflict.");
return file;
}
}

View file

@ -1,134 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
/**
* Clean out the old image directory. Copy the files into the upgrade directory,
* separating into the ones that we translated, and the ones that weren't
* referenced.
*/
public class ImageDirectoryCleaner extends FsuScanner {
private final ImageDirectoryWithBackup imageDirectoryWithBackup;
protected final File translatedDirectory;
protected final File unreferencedDirectory;
public ImageDirectoryCleaner(FSUController controller) {
super(controller);
this.imageDirectoryWithBackup = controller
.getImageDirectoryWithBackup();
this.translatedDirectory = controller.getTranslatedDirectory();
this.unreferencedDirectory = controller.getUnreferencedDirectory();
}
/**
* Remove all of the files from the old image directory.
*/
public void clean(Collection<String> translatedFiles) {
updateLog.section("Cleaning the old image directory of "
+ "files that were translated.");
removeTranslatedFiles(translatedFiles);
updateLog.section("Cleaning the old image directory of "
+ "files that were not referenced.");
removeRemainingFiles(imageDirectoryWithBackup
.getPrimaryImageDirectory());
}
/**
* Move all of the files that we translated into the new system.
*/
private void removeTranslatedFiles(Collection<String> translatedFiles) {
for (String path : translatedFiles) {
File oldFile = new File(
imageDirectoryWithBackup.getPrimaryImageDirectory(), path);
if (oldFile.exists()) {
updateLog.log("moving image file '" + path
+ "' to the 'translated' directory.");
File deletedFile = new File(translatedDirectory, path);
try {
FileUtil.moveFile(oldFile, deletedFile);
} catch (IOException e) {
updateLog.error("Failed to move translated file '"
+ oldFile.getAbsolutePath() + "'");
}
} else {
updateLog.log("Not moving image file '" + path
+ "' to the 'translated' directory -- "
+ "found it in the backup directory.");
}
}
}
/**
* Go through the images directory, and discard any that remain. They must
* not have been referenced by any existing individuals.
*/
private void removeRemainingFiles(File directory) {
updateLog.log("Cleaning image directory '" + directory + "'");
try {
File targetDirectory = makeCorrespondingDirectory(directory);
File[] children = directory.listFiles();
if (children != null) {
for (File child : children) {
if (child.isDirectory()) {
removeRemainingFiles(child);
} else {
moveUnreferencedFile(targetDirectory, child);
}
}
}
} catch (IOException e) {
updateLog.error(
"Failed to clean images directory '"
+ directory.getAbsolutePath() + "'", e);
}
}
/**
* Move this file from its current location to its new home in the
* "unreferenced" directory. Log it.
*/
private void moveUnreferencedFile(File targetDirectory, File file) {
updateLog.log("Moving image file '" + file.getPath()
+ "' to the 'unreferenced' directory");
try {
File newFile = new File(targetDirectory, file.getName());
FileUtil.moveFile(file, newFile);
} catch (IOException e) {
updateLog.error(
"Can't move unreferenced file '" + file.getAbsolutePath()
+ "'", e);
}
}
/**
* Figure out the path from the "images" directory to this one, and create a
* corresponding directory in the "unreferenced" area.
*/
private File makeCorrespondingDirectory(File directory) throws IOException {
String imagesPath = imageDirectoryWithBackup.getPrimaryImageDirectory()
.getAbsolutePath();
String thisPath = directory.getAbsolutePath();
if (!thisPath.startsWith(imagesPath)) {
throw new IOException("Can't make a corresponding directory for '"
+ thisPath + "'");
}
String suffix = thisPath.substring(imagesPath.length());
File corresponding = new File(unreferencedDirectory, suffix);
corresponding.mkdirs();
if (!corresponding.exists()) {
throw new IOException("Failed to create corresponding directory '"
+ corresponding.getAbsolutePath() + "'");
}
return corresponding;
}
}

View file

@ -1,77 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A way to look for files in TOMCAT_WEBAPP/vivo/images, if they are not found
* in upload.directory/images.
*/
public class ImageDirectoryWithBackup {
private static final Log log = LogFactory
.getLog(ImageDirectoryWithBackup.class);
/** The primary image directory, where we do most of the manipulation. */
private final File uploadImageDirectory;
/**
* If we are looking for a file and don't find it in the primary directory,
* look for it here.
*/
private final File webappImageDirectory;
/**
* Be careful! webappImageDirectory may be null.
*/
public ImageDirectoryWithBackup(File uploadImageDirectory,
File webappImageDirectory) {
this.uploadImageDirectory = uploadImageDirectory;
this.webappImageDirectory = webappImageDirectory;
}
/**
* When looking to read a file, start by looking in the
* {@link #uploadImageDirectory}.
*
* If the file isn't found there, look in the {@link #webappImageDirectory}
* as a fallback.
*
* If not there either, return the pointer to the nonexistent file in the
* {@link #uploadImageDirectory}.
*/
File getExistingFile(String relativePath) {
File file1 = new File(uploadImageDirectory, relativePath);
if (file1.exists()) {
log.trace("Found file: " + file1.getAbsolutePath());
return file1;
}
if (webappImageDirectory != null) {
File file2 = new File(webappImageDirectory, relativePath);
if (file2.exists()) {
log.trace("Found file: " + file2.getAbsolutePath());
return file2;
}
}
log.trace("Didn't find file: " + file1.getAbsolutePath());
return file1;
}
/**
* New files will always be created in the primary directory.
*/
File getNewfile(String relativePath) {
return new File(uploadImageDirectory, relativePath);
}
/**
* You can get a direct reference to the primary image directory, but it
* should only be used for directory-base operations, like final cleanup.
*/
public File getPrimaryImageDirectory() {
return uploadImageDirectory;
}
}

View file

@ -1,170 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.io.FilenameUtils;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
/**
* Make copies of the main image and thumbnail in the new file storage system,
* and in the model. Remove the old properties, but don't remove the old files
* yet, in case someone else is referring to them also.
*/
public class ImageSchemaTranslater extends FsuScanner {
private final ImageDirectoryWithBackup imageDirectoryWithBackup;
protected final FileStorage fileStorage;
protected final UploadedFileHelper uploadedFileHelper;
public ImageSchemaTranslater(FSUController controller) {
super(controller);
this.imageDirectoryWithBackup = controller
.getImageDirectoryWithBackup();
this.fileStorage = controller.getFileStorage();
this.uploadedFileHelper = controller.getUploadedFileHelper();
}
/**
* By the time we get here, any individual with a main image also has a
* thumbnail, and vice versa, and exactly one of each. For each one,
* translate the main image and the thumbnail into the new system.
*/
public Collection<String> translate() {
updateLog.section("Copying images into the new file storage, "
+ "and adding them to the new model.");
SortedSet<String> translated = new TreeSet<String>();
ResIterator haveImage = model.listResourcesWithProperty(imageProperty);
try {
while (haveImage.hasNext()) {
Resource resource = haveImage.next();
translateImages(resource, translated);
}
} finally {
haveImage.close();
}
return translated;
}
/**
* This individual should have exactly one main image and exactly one
* thumbnail.
* <ul>
* <li>Translate the first main image into the new system.</li>
* <li>Translate the first thumbnail into the new system.</li>
* <li>Remove all old-style main image properties.</li>
* <li>Remove all old-style thumbnail properties.</li>
* </ul>
*/
private void translateImages(Resource resource,
Collection<String> translated) {
List<String> mainImages = getValues(resource, imageProperty);
if (mainImages.size() != 1) {
updateLog.error(resource, "has " + mainImages.size()
+ " main images: " + mainImages);
return;
}
List<String> thumbnails = getValues(resource, thumbProperty);
if (thumbnails.size() != 1) {
updateLog.error(resource, "has " + thumbnails.size()
+ " thumbnails: " + thumbnails);
return;
}
FileInfo main = translateFile(resource, mainImages.get(0), "main image");
FileInfo thumb = translateFile(resource, thumbnails.get(0), "thumbnail");
if ((main == null) || (thumb == null)) {
return;
}
uploadedFileHelper.setImagesOnEntity(resource.getURI(), main, thumb);
translated.add(mainImages.get(0));
ResourceWrapper.removeAll(resource, imageProperty);
translated.add(thumbnails.get(0));
ResourceWrapper.removeAll(resource, thumbProperty);
}
/**
* Translate an image file into the new system
* <ul>
* <li>Attempt to infer MIME type.</li>
* <li>Copy into the File system.</li>
* <li>Create the File and Bytestream individuals in the model.</li>
* </ul>
*/
private FileInfo translateFile(Resource resource, String path, String label) {
File oldFile = imageDirectoryWithBackup.getExistingFile(path);
String filename = getSimpleFilename(path);
String mimeType = guessMimeType(resource, filename);
InputStream inputStream = null;
try {
inputStream = new FileInputStream(oldFile);
// Create the file individuals in the model
FileInfo fileInfo = uploadedFileHelper.createFile(filename,
mimeType, inputStream);
updateLog.log(resource, "translating " + label + " '" + path
+ "' into the file storage as '" + fileInfo.getUri() + "'");
return fileInfo;
} catch (IOException e) {
updateLog.error(resource, "Can't create the " + label + " file. ",
e);
return null;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Remove any path parts, and just get the filename and extension.
*/
private String getSimpleFilename(String path) {
return FilenameUtils.getName(path);
}
/**
* Guess what the MIME type might be.
*/
private String guessMimeType(Resource resource, String filename) {
if (filename.endsWith(".gif") || filename.endsWith(".GIF")) {
return "image/gif";
} else if (filename.endsWith(".png") || filename.endsWith(".PNG")) {
return "image/png";
} else if (filename.endsWith(".jpg") || filename.endsWith(".JPG")) {
return "image/jpeg";
} else if (filename.endsWith(".jpeg") || filename.endsWith(".JPEG")) {
return "image/jpeg";
} else if (filename.endsWith(".jpe") || filename.endsWith(".JPE")) {
return "image/jpeg";
} else {
updateLog.warn(resource,
"can't recognize the MIME type of this image file: '"
+ filename + "'");
return "image";
}
}
}

View file

@ -1,54 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.shared.Lock;
/**
* Utility methods that operate against the Model with proper locks.
*/
public class ModelWrapper {
public static Collection<Resource> listResourcesWithProperty(Model model,
Property property) {
List<Resource> list = new ArrayList<Resource>();
ResIterator iterator = model.listResourcesWithProperty(property);
try {
while (iterator.hasNext()) {
Resource resource = iterator.next();
list.add(resource);
}
} finally {
iterator.close();
}
return list;
}
public static void removeStatement(Model model, Statement stmt) {
model.enterCriticalSection(Lock.WRITE);
try {
model.remove(stmt);
} finally {
model.leaveCriticalSection();
}
}
public static void add(Model model, Resource subject, Property predicate,
String value) {
model.enterCriticalSection(Lock.WRITE);
try {
model.add(subject, predicate, value);
} finally {
model.leaveCriticalSection();
}
}
}

View file

@ -1,65 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.util.List;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
/**
* If a resource has more than one image or more than one thumbnail, this
* discards the extras.
*/
public class MultiplePropertyRemover extends FsuScanner {
public MultiplePropertyRemover(FSUController controller) {
super(controller);
}
/**
* By now, we have removed any non-literals or dead ends, so keep the first
* one and discard any extras.
*/
public void remove() {
updateLog.section("Checking for resources with more "
+ "than one main image, or more than one thumbnail.");
removeExtraProperties(imageProperty, "main image");
removeExtraProperties(thumbProperty, "thumbnail");
}
/**
* Check each resource that has this property.
*/
public void removeExtraProperties(Property prop, String label) {
for (Resource resource : ModelWrapper.listResourcesWithProperty(model,
prop)) {
removeExtraProperties(resource, prop, label);
}
}
/**
* If this resource has more than one of this property, delete the extras.
*/
private void removeExtraProperties(Resource resource, Property prop,
String label) {
List<Statement> stmts = getStatements(resource, prop);
for (int i = 1; i < stmts.size(); i++) {
Statement stmt = stmts.get(i);
RDFNode node = stmt.getObject();
if (node.isLiteral()) {
String value = ((Literal) node).getString();
updateLog.warn(resource, "removing extra " + label
+ " property: '" + value + "'");
} else {
updateLog.warn(resource, "removing extra " + label
+ " property: '" + node + "'");
}
ModelWrapper.removeStatement(model, stmt);
}
}
}

View file

@ -1,173 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_HEIGHT;
import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.THUMBNAIL_WIDTH;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;
import com.hp.hpl.jena.rdf.model.Resource;
import com.sun.media.jai.codec.MemoryCacheSeekableStream;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.CropRectangle;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadThumbnailer;
/**
* Adjust any individual that has a main image but no thumbnail.
*/
public class NoThumbsAdjuster extends FsuScanner {
private ImageDirectoryWithBackup imageDirectoryWithBackup;
public NoThumbsAdjuster(FSUController controller) {
super(controller);
this.imageDirectoryWithBackup = controller
.getImageDirectoryWithBackup();
}
/**
* For every individual with main images but no thumbnails, create a
* thumbnail from the first main image.
*/
public void adjust() {
updateLog.section("Creating thumbnails to match main images.");
for (Resource resource : ModelWrapper.listResourcesWithProperty(model,
imageProperty)) {
if (resource.getProperty(thumbProperty) == null) {
createThumbnailFromMainImage(resource);
}
}
}
/**
* This individual has a main image but no thumbnail. Create one.
* <ul>
* <li>Figure a name for the thumbnail image.</li>
* <li>Make a scaled copy of the main image into the thumbnail.</li>
* <li>Set that file as a thumbnail (old-style) on the individual.</li>
* </ul>
*/
private void createThumbnailFromMainImage(Resource resource) {
String mainFilename = getValues(resource, imageProperty).get(0);
String thumbFilename = addFilenamePrefix("_thumbnail_", mainFilename);
updateLog.log(resource, "creating a thumbnail at '" + thumbFilename
+ "' from the main image at '" + mainFilename + "'");
File mainFile = imageDirectoryWithBackup.getExistingFile(mainFilename);
File thumbFile = imageDirectoryWithBackup.getNewfile(thumbFilename);
thumbFile = checkNameConflicts(thumbFile);
try {
CropRectangle crop = getImageSize(mainFile);
if (imageIsSmallEnoughAlready(crop)) {
copyMainImageToThumbnail(mainFile, thumbFile);
} else {
cropScaleAndStore(crop, mainFile, thumbFile);
}
ResourceWrapper.addProperty(resource, thumbProperty, thumbFilename);
} catch (IOException e) {
updateLog.error(resource, "failed to create thumbnail file '"
+ thumbFilename + "'", e);
}
}
private CropRectangle getImageSize(File file) throws IOException {
InputStream imageSource = null;
try {
imageSource = new FileInputStream(file);
MemoryCacheSeekableStream stream = new MemoryCacheSeekableStream(
imageSource);
RenderedOp image = JAI.create("stream", stream);
return new CropRectangle(0, 0, image.getHeight(), image.getWidth());
} finally {
if (imageSource != null) {
try {
imageSource.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private boolean imageIsSmallEnoughAlready(CropRectangle crop) {
return (crop.height <= THUMBNAIL_HEIGHT)
&& (crop.width <= THUMBNAIL_WIDTH);
}
private void copyMainImageToThumbnail(File mainFile, File thumbFile)
throws IOException {
InputStream imageSource = null;
try {
imageSource = new FileInputStream(mainFile);
storeImage(imageSource, thumbFile);
} finally {
if (imageSource != null) {
try {
imageSource.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void cropScaleAndStore(CropRectangle crop, File mainFile,
File thumbFile) throws IOException {
InputStream mainImageStream = null;
InputStream imageSource = null;
try {
mainImageStream = new FileInputStream(mainFile);
ImageUploadThumbnailer iut = new ImageUploadThumbnailer(
THUMBNAIL_HEIGHT, THUMBNAIL_WIDTH);
imageSource = iut.cropAndScale(mainImageStream, crop);
storeImage(imageSource, thumbFile);
} finally {
if (mainImageStream != null) {
try {
mainImageStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (imageSource != null) {
try {
imageSource.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void storeImage(InputStream source, File file) throws IOException {
OutputStream sink = null;
try {
sink = new FileOutputStream(file);
byte[] buffer = new byte[8192];
int howMany;
while (-1 != (howMany = source.read(buffer))) {
sink.write(buffer, 0, howMany);
}
} finally {
if (sink != null) {
try {
sink.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

View file

@ -1,71 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.util.ArrayList;
import java.util.List;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.shared.Lock;
/**
* All image properties should have literal values. Burn any that don't.
*/
public class NonLiteralPropertyRemover extends FsuScanner {
public NonLiteralPropertyRemover(FSUController controller) {
super(controller);
}
/**
* Remove any image properties whose objects are not {@link Literal}s.
*/
public void remove() {
updateLog.section("Checking for image properties whose objects "
+ "are not literals.");
removeNonLiterals(imageProperty, "image file");
removeNonLiterals(thumbProperty, "thumbnail");
}
/**
* Check all resources for bogus values on this property.
*/
private void removeNonLiterals(Property prop, String label) {
for (Resource resource : ModelWrapper.listResourcesWithProperty(model,
prop)) {
removeNonLiterals(resource, prop, label);
}
}
/**
* Check this resource for bogus values onthis property.
*/
private void removeNonLiterals(Resource resource, Property prop,
String label) {
List<RDFNode> bogusValues = new ArrayList<RDFNode>();
for (Statement stmt : ResourceWrapper.listProperties(resource, prop)) {
RDFNode object = stmt.getObject();
if (!object.isLiteral()) {
bogusValues.add(object);
}
}
for (RDFNode bogusValue : bogusValues) {
updateLog.warn(resource, "discarding " + label
+ " property with non-literal as object: '" + bogusValue
+ "'");
model.enterCriticalSection(Lock.WRITE);
try {
model.createStatement(resource, prop, bogusValue).remove();
} finally {
model.leaveCriticalSection();
}
}
}
}

View file

@ -1,63 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.shared.Lock;
/**
* Utility methods that get the appropriate model locks before manipluating
* resources.
*/
public class ResourceWrapper {
public static Statement getProperty(Resource resource, Property property) {
resource.getModel().enterCriticalSection(Lock.READ);
try {
return resource.getProperty(property);
} finally {
resource.getModel().leaveCriticalSection();
}
}
public static void addProperty(Resource resource, Property property,
String value) {
resource.getModel().enterCriticalSection(Lock.WRITE);
try {
resource.addProperty(property, value);
} finally {
resource.getModel().leaveCriticalSection();
}
}
public static void removeAll(Resource resource, Property property) {
resource.getModel().enterCriticalSection(Lock.WRITE);
try {
resource.removeAll(property);
} finally {
resource.getModel().leaveCriticalSection();
}
}
public static Collection<Statement> listProperties(Resource resource,
Property prop) {
List<Statement> list = new ArrayList<Statement>();
StmtIterator stmts = resource.listProperties(prop);
try {
while (stmts.hasNext()) {
list.add(stmts.next());
}
return list;
} finally {
stmts.close();
}
}
}