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:
parent
acf1b1896a
commit
04eb46415a
16 changed files with 0 additions and 2014 deletions
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
|
|
||||||
}
|
|
|
@ -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";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue