NIHVIVO-737 Clean up file logic. Create a FileInfo class that holds all relevant info about an uploaded File (mimeType, filename, URI, bytestream). Create an ImageInfo class that holds to FileInfo objects. Create factory methods for each of these classes. Client code becomes much simpler.

This commit is contained in:
jeb228 2010-08-05 14:55:40 +00:00
parent ad65c27471
commit 9311e83827
15 changed files with 664 additions and 781 deletions

View file

@ -5,7 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.beans;
import org.json.JSONException;
import org.json.JSONObject;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
import java.lang.reflect.Method;
import java.sql.Timestamp;
@ -49,8 +49,7 @@ public class IndividualImpl extends BaseResourceBean implements Individual, Comp
protected String anchor = null;
protected String blurb = null;
protected String mainImageUri = NOT_INITIALIZED;
protected String imageUrl;
protected String thumbUrl;
protected ImageInfo imageInfo = null;
protected int statusId = 0;
protected String status = null;
protected List <Link>linksList = null;
@ -281,8 +280,7 @@ public class IndividualImpl extends BaseResourceBean implements Individual, Comp
@Override
public void setMainImageUri(String mainImageUri) {
this.mainImageUri = mainImageUri;
this.imageUrl = null;
this.thumbUrl = null;
this.imageInfo = null;
}
@Override

View file

@ -41,10 +41,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.Portal;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQuery;
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryWrapper;
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
@ -102,7 +99,7 @@ public class EntityController extends VitroHttpServlet {
}
// If this is an uploaded file, redirect to its "alias URL".
String aliasUrl = getAliasUrlForBytestreamIndividual(indiv);
String aliasUrl = getAliasUrlForBytestreamIndividual(req, indiv);
if (aliasUrl != null) {
res.sendRedirect(req.getContextPath() + aliasUrl);
return;
@ -491,39 +488,16 @@ public class EntityController extends VitroHttpServlet {
* If this entity represents a File Bytestream, get its alias URL so we can
* properly serve the file contents.
*/
private String getAliasUrlForBytestreamIndividual(Individual entity)
private String getAliasUrlForBytestreamIndividual(HttpServletRequest req, Individual entity)
throws IOException {
if (!FileModelHelper.isFileBytestream(entity)) {
log.debug("Entity at '" + entity.getURI()
+ "' is not recognized as a FileByteStream.");
return null;
}
FileStorage fs = (FileStorage) getServletContext().getAttribute(
FileStorageSetup.ATTRIBUTE_NAME);
if (fs == null) {
log.error("Servlet context does not contain file storage at '"
+ FileStorageSetup.ATTRIBUTE_NAME + "'");
return null;
}
String filename = fs.getFilename(entity.getURI());
if (filename == null) {
log.error("Entity at '" + entity.getURI()
+ "' is recognized as a FileByteStream, "
+ "but the file system does not recognize it.");
return null;
}
String url = FileServingHelper.getBytestreamAliasUrl(entity.getURI(),
filename);
if (url.equals(entity.getURI())) {
log.error("Entity at '" + entity.getURI()
+ "' is recognized as a FileByteStream, "
+ "but can't be translated to an alias URL.");
FileInfo fileInfo = FileInfo.instanceFromBytestreamUri(new VitroRequest(
req).getWebappDaoFactory(), entity.getURI());
if (fileInfo == null) {
log.trace("Entity '" + entity.getURI() + "' is not a bytestream.");
return null;
}
String url = fileInfo.getBytestreamAliasUrl();
log.debug("Alias URL for '" + entity.getURI() + "' is '" + url + "'");
return url;
}

View file

@ -25,11 +25,10 @@ import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
import freemarker.template.Configuration;
@ -196,8 +195,8 @@ public class ImageUploadController extends FreemarkerHttpServlet {
// Add the values that we got, and merge to the template.
body.putAll(values.getBodyMap());
root.put("body", mergeBodyToTemplate(values.getTemplateName(), body,
config));
root.put("body",
mergeBodyToTemplate(values.getTemplateName(), body, config));
// Continue to simulate FreeMarkerHttpServlet.doGet()
root.put("title", body.get("title"));
@ -276,11 +275,12 @@ public class ImageUploadController extends FreemarkerHttpServlet {
* Show the first screen in the upload process: Add or Replace.
*/
private ResponseValues doIntroScreen(VitroRequest vreq, Individual entity) {
String thumbUrl = getThumbnailUrl(entity);
if (thumbUrl == null) {
ImageInfo imageInfo = ImageInfo.instanceFromEntityUri(
vreq.getFullWebappDaoFactory(), entity);
if (imageInfo == null) {
return showAddImagePage(vreq, entity);
} else {
return showReplaceImagePage(vreq, entity, thumbUrl);
return showReplaceImagePage(vreq, entity, imageInfo);
}
}
@ -289,8 +289,8 @@ public class ImageUploadController extends FreemarkerHttpServlet {
* image (and thumbnail), and attach the new main image.
*/
private ResponseValues doUploadImage(VitroRequest vreq, Individual entity) {
ImageUploadHelper helper = new ImageUploadHelper(fileStorage, vreq
.getFullWebappDaoFactory());
ImageUploadHelper helper = new ImageUploadHelper(fileStorage,
vreq.getFullWebappDaoFactory());
try {
// Did they provide a file to upload? If not, show an error.
@ -303,8 +303,8 @@ public class ImageUploadController extends FreemarkerHttpServlet {
Dimensions size = helper.getNewImageSize(fileInfo);
// Go to the cropping page.
return showCropImagePage(vreq, entity, fileInfo
.getBytestreamAliasUrl(), size);
return showCropImagePage(vreq, entity,
fileInfo.getBytestreamAliasUrl(), size);
} catch (UserMistakeException e) {
return showErrorMessage(vreq, entity, e.getMessage());
}
@ -316,11 +316,12 @@ public class ImageUploadController extends FreemarkerHttpServlet {
*/
private ResponseValues showErrorMessage(VitroRequest vreq,
Individual entity, String message) {
String thumbUrl = getThumbnailUrl(entity);
if (thumbUrl == null) {
ImageInfo imageInfo = ImageInfo.instanceFromEntityUri(
vreq.getFullWebappDaoFactory(), entity);
if (imageInfo == null) {
return showAddImagePageWithError(vreq, entity, message);
} else {
return showReplaceImagePageWithError(vreq, entity, thumbUrl,
return showReplaceImagePageWithError(vreq, entity, imageInfo,
message);
}
}
@ -331,8 +332,8 @@ public class ImageUploadController extends FreemarkerHttpServlet {
*/
private ResponseValues doCreateThumbnail(VitroRequest vreq,
Individual entity) {
ImageUploadHelper helper = new ImageUploadHelper(fileStorage, vreq
.getFullWebappDaoFactory());
ImageUploadHelper helper = new ImageUploadHelper(fileStorage,
vreq.getFullWebappDaoFactory());
try {
CropRectangle crop = validateCropCoordinates(vreq);
@ -353,8 +354,8 @@ public class ImageUploadController extends FreemarkerHttpServlet {
* page.
*/
private ResponseValues doDeleteImage(VitroRequest vreq, Individual entity) {
ImageUploadHelper helper = new ImageUploadHelper(fileStorage, vreq
.getFullWebappDaoFactory());
ImageUploadHelper helper = new ImageUploadHelper(fileStorage,
vreq.getFullWebappDaoFactory());
helper.removeExistingImage(entity);
@ -366,8 +367,8 @@ public class ImageUploadController extends FreemarkerHttpServlet {
* screen.
*/
private ResponseValues doDeleteThenEdit(VitroRequest vreq, Individual entity) {
ImageUploadHelper helper = new ImageUploadHelper(fileStorage, vreq
.getFullWebappDaoFactory());
ImageUploadHelper helper = new ImageUploadHelper(fileStorage,
vreq.getFullWebappDaoFactory());
helper.removeExistingImage(entity);
@ -425,15 +426,6 @@ public class ImageUploadController extends FreemarkerHttpServlet {
}
}
/**
* Get the URL that will serve this entity's thumbnail image, or null.
*/
private String getThumbnailUrl(Individual entity) {
String thumbUri = FileModelHelper.getThumbnailBytestreamUri(entity);
String thumbFilename = FileModelHelper.getThumbnailFilename(entity);
return FileServingHelper.getBytestreamAliasUrl(thumbUri, thumbFilename);
}
/**
* The individual has no image - go to the Add Image page.
*
@ -444,8 +436,8 @@ public class ImageUploadController extends FreemarkerHttpServlet {
Individual entity) {
String formAction = (entity == null) ? "" : formAction(entity.getURI(),
ACTION_UPLOAD);
String cancelUrl = (entity == null) ? "" : exitPageUrl(vreq, entity
.getURI());
String cancelUrl = (entity == null) ? "" : exitPageUrl(vreq,
entity.getURI());
TemplateResponseValues rv = new TemplateResponseValues(TEMPLATE_NEW);
rv.put(BODY_THUMBNAIL_URL, UrlBuilder.getUrl(DUMMY_THUMBNAIL_URL));
@ -467,10 +459,11 @@ public class ImageUploadController extends FreemarkerHttpServlet {
* The individual has an image - go to the Replace Image page.
*/
private TemplateResponseValues showReplaceImagePage(VitroRequest vreq,
Individual entity, String thumbUrl) {
Individual entity, ImageInfo imageInfo) {
TemplateResponseValues rv = new TemplateResponseValues(TEMPLATE_REPLACE);
rv.put(BODY_THUMBNAIL_URL, UrlBuilder.getUrl(thumbUrl));
rv.put(BODY_DELETE_URL, formAction(entity.getURI(), ACTION_DELETE_EDIT));
rv.put(BODY_THUMBNAIL_URL, UrlBuilder.getUrl(imageInfo.getThumbnail()
.getBytestreamAliasUrl()));
rv.put(BODY_DELETE_URL, formAction(entity.getURI(), ACTION_DELETE));
rv.put(BODY_FORM_ACTION, formAction(entity.getURI(), ACTION_UPLOAD));
rv.put(BODY_CANCEL_URL, exitPageUrl(vreq, entity.getURI()));
rv.put(BODY_TITLE, "Replace image" + forName(entity));
@ -481,9 +474,10 @@ public class ImageUploadController extends FreemarkerHttpServlet {
* The individual has an image, but the user did something wrong.
*/
private TemplateResponseValues showReplaceImagePageWithError(
VitroRequest vreq, Individual entity, String thumbUrl,
VitroRequest vreq, Individual entity, ImageInfo imageInfo,
String message) {
TemplateResponseValues rv = showReplaceImagePage(vreq, entity, thumbUrl);
TemplateResponseValues rv = showReplaceImagePage(vreq, entity,
imageInfo);
rv.put(BODY_ERROR_MESSAGE, message);
return rv;
}

View file

@ -40,8 +40,8 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadControl
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.Dimensions;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.UserMistakeException;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.TempFileHolder;
import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
@ -87,14 +87,13 @@ public class ImageUploadHelper {
new NonNoisyImagingListener());
}
private final WebappDaoFactory webAppDaoFactory;
private final FileModelHelper fileModelHelper;
private final FileStorage fileStorage;
private final UploadedFileHelper uploadedFileHelper;
ImageUploadHelper(FileStorage fileStorage, WebappDaoFactory webAppDaoFactory) {
this.webAppDaoFactory = webAppDaoFactory;
this.fileModelHelper = new FileModelHelper(webAppDaoFactory);
this.fileStorage = fileStorage;
this.uploadedFileHelper = new UploadedFileHelper(fileStorage,
webAppDaoFactory);
}
/**
@ -162,10 +161,8 @@ public class ImageUploadHelper {
inputStream = fileItem.getInputStream();
String mimeType = getMimeType(fileItem);
String filename = getSimpleFilename(fileItem);
WebappDaoFactory wadf = vreq.getWebappDaoFactory();
FileInfo fileInfo = FileModelHelper.createFile(fileStorage, wadf,
filename, mimeType, inputStream);
FileInfo fileInfo = uploadedFileHelper.createFile(filename,
mimeType, inputStream);
TempFileHolder.attach(vreq.getSession(), ATTRIBUTE_TEMP_FILE,
fileInfo);
@ -274,9 +271,9 @@ public class ImageUploadHelper {
String mimeType = RECOGNIZED_FILE_TYPES.get(".jpg");
String filename = createThumbnailFilename(mainFilename);
FileInfo fileInfo = uploadedFileHelper.createFile(filename,
mimeType, thumbStream);
FileInfo fileInfo = FileModelHelper.createFile(fileStorage,
webAppDaoFactory, filename, mimeType, thumbStream);
log.debug("Created thumbnail: " + fileInfo);
return fileInfo;
} catch (FileAlreadyExistsException e) {
@ -304,64 +301,11 @@ public class ImageUploadHelper {
}
/**
* If this entity already had a main image, remove the connection. If the
* image and the thumbnail are no longer used by anyone, remove them from
* the model, and from the file system.
* If this entity already had a main image, remove it. If the image and the
* thumbnail are no longer used by anyone, throw them away.
*/
void removeExistingImage(Individual person) {
Individual mainImage = fileModelHelper.removeMainImage(person);
if (mainImage == null) {
return;
}
removeExistingThumbnail(person);
if (!fileModelHelper.isFileReferenced(mainImage)) {
Individual bytes = FileModelHelper.getBytestreamForFile(mainImage);
if (bytes != null) {
try {
fileStorage.deleteFile(bytes.getURI());
} catch (IOException e) {
throw new IllegalStateException(
"Can't delete the main image file: '"
+ bytes.getURI() + "' for '"
+ person.getName() + "' ("
+ person.getURI() + ")", e);
}
}
fileModelHelper.removeFileFromModel(mainImage);
}
}
/**
* If the entity already has a thumbnail, remove it. If there are no other
* references to the thumbnail, delete it from the model and from the file
* system.
*/
void removeExistingThumbnail(Individual person) {
Individual mainImage = FileModelHelper.getMainImage(person);
Individual thumbnail = FileModelHelper.getThumbnailForImage(mainImage);
if (thumbnail == null) {
return;
}
fileModelHelper.removeThumbnail(person);
if (!fileModelHelper.isFileReferenced(thumbnail)) {
Individual bytes = FileModelHelper.getBytestreamForFile(thumbnail);
if (bytes != null) {
try {
fileStorage.deleteFile(bytes.getURI());
} catch (IOException e) {
throw new IllegalStateException(
"Can't delete the thumbnail file: '"
+ bytes.getURI() + "' for '"
+ person.getName() + "' ("
+ person.getURI() + ")", e);
}
}
fileModelHelper.removeFileFromModel(thumbnail);
}
uploadedFileHelper.removeMainImage(person);
}
/**
@ -369,7 +313,7 @@ public class ImageUploadHelper {
*/
void storeImageFiles(Individual entity, FileInfo newImage,
FileInfo thumbnail) {
FileModelHelper.setImagesOnEntity(webAppDaoFactory, entity, newImage,
uploadedFileHelper.setImagesOnEntity(entity.getURI(), newImage,
thumbnail);
}

View file

@ -43,10 +43,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQuery;
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryWrapper;
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
@ -114,7 +111,7 @@ public class IndividualController extends FreemarkerHttpServlet {
}
// If this is an uploaded file, redirect to its "alias URL".
String aliasUrl = getAliasUrlForBytestreamIndividual(indiv);
String aliasUrl = getAliasUrlForBytestreamIndividual(req, indiv);
if (aliasUrl != null) {
res.sendRedirect(req.getContextPath() + aliasUrl);
return;
@ -499,43 +496,20 @@ public class IndividualController extends FreemarkerHttpServlet {
return false;
}
/**
/**
* If this entity represents a File Bytestream, get its alias URL so we can
* properly serve the file contents.
*/
private String getAliasUrlForBytestreamIndividual(Individual entity)
private String getAliasUrlForBytestreamIndividual(HttpServletRequest req, Individual entity)
throws IOException {
if (!FileModelHelper.isFileBytestream(entity)) {
log.debug("Entity at '" + entity.getURI()
+ "' is not recognized as a FileByteStream.");
return null;
}
FileStorage fs = (FileStorage) getServletContext().getAttribute(
FileStorageSetup.ATTRIBUTE_NAME);
if (fs == null) {
log.error("Servlet context does not contain file storage at '"
+ FileStorageSetup.ATTRIBUTE_NAME + "'");
return null;
}
String filename = fs.getFilename(entity.getURI());
if (filename == null) {
log.error("Entity at '" + entity.getURI()
+ "' is recognized as a FileByteStream, "
+ "but the file system does not recognize it.");
return null;
}
String url = FileServingHelper.getBytestreamAliasUrl(entity.getURI(),
filename);
if (url.equals(entity.getURI())) {
log.error("Entity at '" + entity.getURI()
+ "' is recognized as a FileByteStream, "
+ "but can't be translated to an alias URL.");
FileInfo fileInfo = FileInfo.instanceFromBytestreamUri(new VitroRequest(
req).getWebappDaoFactory(), entity.getURI());
if (fileInfo == null) {
log.trace("Entity '" + entity.getURI() + "' is not a bytestream.");
return null;
}
String url = fileInfo.getBytestreamAliasUrl();
log.debug("Alias URL for '" + entity.getURI() + "' is '" + url + "'");
return url;
}

View file

@ -41,8 +41,7 @@ import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.dao.VClassDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
import edu.cornell.mannlib.vitro.webapp.utils.FlagMathUtils;
public class IndividualJena extends IndividualImpl implements Individual {
@ -496,38 +495,33 @@ public class IndividualJena extends IndividualImpl implements Individual {
@Override
public String getImageUrl() {
if (this.imageUrl != null) {
log.debug("imageUrl was cached for " + getURI() + ": '"
+ this.imageUrl + "'");
return imageUrl;
} else {
String imageUri = FileModelHelper.getMainImageBytestreamUri(this);
String filename = FileModelHelper.getMainImageFilename(this);
imageUrl = FileServingHelper.getBytestreamAliasUrl(imageUri,
filename);
log.debug("figured imageUrl for " + getURI() + ": '"
+ this.imageUrl + "'");
return imageUrl;
if (this.imageInfo == null) {
this.imageInfo = ImageInfo.instanceFromEntityUri(webappDaoFactory, this);
log.trace("figured imageInfo for " + getURI() + ": '"
+ this.imageInfo + "'");
}
if (this.imageInfo == null) {
this.imageInfo = ImageInfo.EMPTY_IMAGE_INFO;
log.trace("imageInfo for " + getURI() + " is empty.");
}
return this.imageInfo.getMainImage().getBytestreamAliasUrl();
}
@Override
public String getThumbUrl() {
if (this.thumbUrl != null) {
log.debug("thumbUrl was cached for " + getURI() + ": '"
+ this.thumbUrl + "'");
return thumbUrl;
} else {
String imageUri = FileModelHelper.getThumbnailBytestreamUri(this);
String filename = FileModelHelper.getThumbnailFilename(this);
thumbUrl = FileServingHelper.getBytestreamAliasUrl(imageUri, filename);
log.debug("figured thumbUrl for " + getURI() + ": '"
+ this.thumbUrl + "'");
return thumbUrl;
if (this.imageInfo == null) {
this.imageInfo = ImageInfo.instanceFromEntityUri(webappDaoFactory, this);
log.trace("figured imageInfo for " + getURI() + ": '"
+ this.imageInfo + "'");
}
if (this.imageInfo == null) {
this.imageInfo = ImageInfo.EMPTY_IMAGE_INFO;
log.trace("imageInfo for " + getURI() + " is empty.");
}
return this.imageInfo.getThumbnail().getBytestreamAliasUrl();
}
public String getAnchor() {
public String getAnchor() {
if (this.anchor != null) {
return anchor;
} else {

View file

@ -1,500 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.IndividualImpl;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatementImpl;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.InsertException;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
/**
* <p>
* A collection of methods to help manipulate the model, with regard to uploaded
* files.
* </p>
* <p>
* Some of the public methods are static, since the Individual that is passed as
* a parameter holds all necessary references for the operation. Other methods
* require an instance, which is initialized with a {@link WebappDaoFactory}.
* </p>
* <p>
* TODO: This should be based around FileInfo and ImageInfo, as much as
* possible.
* </p>
*/
public class FileModelHelper {
private static final Log log = LogFactory.getLog(FileModelHelper.class);
// ----------------------------------------------------------------------
// Methods based around FileInfo
// ----------------------------------------------------------------------
public static FileInfo createFile(FileStorage fileStorage,
WebappDaoFactory wadf, String filename, String mimeType,
InputStream inputStream) throws FileAlreadyExistsException,
IOException {
FileModelHelper fmh = new FileModelHelper(wadf);
// Create the file individuals in the model
Individual byteStream = fmh.createByteStreamIndividual();
String bytestreamUri = byteStream.getURI();
Individual file = fmh.createFileIndividual(mimeType, filename,
byteStream);
String fileUri = file.getURI();
// Store the file in the FileStorage system.
fileStorage.createFile(bytestreamUri, filename, inputStream);
// Figure out the alias URL
String aliasUrl = FileServingHelper.getBytestreamAliasUrl(
bytestreamUri, filename);
// And wrap it all up in a tidy little package.
return new FileInfo.Builder().setFilename(filename).setMimeType(
mimeType).setUri(fileUri).setBytestreamUri(bytestreamUri)
.setBytestreamAliasUrl(aliasUrl).build();
}
/**
* Record this image file and thumbnail on this entity. NOTE: after this
* update, the entity object is stale.
*/
public static void setImagesOnEntity(WebappDaoFactory wadf,
Individual entity, FileInfo mainInfo, FileInfo thumbInfo) {
IndividualDao individualDao = wadf.getIndividualDao();
// Add the thumbnail file to the main image file.
ObjectPropertyStatementDao opsd = wadf.getObjectPropertyStatementDao();
opsd.insertNewObjectPropertyStatement(new ObjectPropertyStatementImpl(
mainInfo.getUri(), VitroVocabulary.FS_THUMBNAIL_IMAGE,
thumbInfo.getUri()));
// Add the main image file to the entity.
entity.setMainImageUri(mainInfo.getUri());
individualDao.updateIndividual(entity);
log.debug("Set images on '" + entity.getURI() + "': main=" + mainInfo
+ ", thumb=" + thumbInfo);
}
// ----------------------------------------------------------------------
// Static methods -- the Individual holds all necessary references.
// ----------------------------------------------------------------------
/**
* Is this a FileByteStream individual?
*/
public static boolean isFileBytestream(Individual entity) {
for (VClass vClass : entity.getVClasses()) {
if (VitroVocabulary.FS_BYTESTREAM_CLASS.equals(vClass.getURI())) {
log.debug("Entity '" + entity.getURI() + "' is a bytestream");
return true;
}
}
log.debug("Entity '" + entity.getURI() + "' is not a bytestream");
return false;
}
/**
* Locate the file surrogate for the main image of this entity.
*
* @return the surrogate, or <code>null</code> if there is no such image, or
* if the entity itself is <code>null</code>.
*/
public static Individual getMainImage(Individual entity) {
if (entity == null) {
return null;
}
Individual mainFile = entity
.getRelatedIndividual(VitroVocabulary.IND_MAIN_IMAGE);
if (mainFile == null) {
log.debug("Entity '" + entity.getURI()
+ "' had no associated main image: mainImageURI="
+ entity.getMainImageUri());
return null;
} else {
log.debug("Entity '" + entity.getURI()
+ "' had associated main image: '" + mainFile.getURI()
+ "'");
return mainFile;
}
}
/**
* Locate the file surrogate for the thumbnail of this file.
*
* @return the surrogate, or <code>null</code> if there is no thumbnail, or
* if the file itself is <code>null</code>.
*/
public static Individual getThumbnailForImage(Individual fileSurrogate) {
if (fileSurrogate == null) {
return null;
}
Individual thumbFile = fileSurrogate
.getRelatedIndividual(VitroVocabulary.FS_THUMBNAIL_IMAGE);
if (thumbFile == null) {
log.warn("Main image file '" + fileSurrogate.getURI()
+ "' had no associated thumbnail.");
return null;
} else {
log.debug("Main image file '" + fileSurrogate.getURI()
+ "' had associated thumbnail: '" + thumbFile.getURI()
+ "'");
return thumbFile;
}
}
/**
* Locate the bytestream object for this file.
*
* @return the bytestream object, or <code>null</code> if there is no
* bytestream, or if the file itself is <code>null</code>.
*/
public static Individual getBytestreamForFile(Individual fileSurrogate) {
if (fileSurrogate == null) {
return null;
}
Individual byteStream = fileSurrogate
.getRelatedIndividual(VitroVocabulary.FS_DOWNLOAD_LOCATION);
if (byteStream == null) {
log.error("File surrogate '" + fileSurrogate.getURI()
+ "' had no associated bytestream.");
return null;
} else {
log.debug("File surroage'" + fileSurrogate.getURI()
+ "' had associated bytestream: '" + byteStream.getURI()
+ "'");
return byteStream;
}
}
/**
* Find the filename for this file.
*
* @return the filename, or <code>null</code> if the file itself is
* <code>null</code>.
*/
public static String getFilename(Individual fileSurrogate) {
if (fileSurrogate == null) {
return null;
}
String filename = fileSurrogate
.getDataValue(VitroVocabulary.FS_FILENAME);
if (filename == null) {
log.error("File had no filename: '" + fileSurrogate.getURI() + "'");
} else {
log.debug("Filename for '" + fileSurrogate.getURI() + "' was '"
+ filename + "'");
}
return filename;
}
/**
* Find the MIME type for this file.
*
* @return the MIME type, or <code>null</code> if the file itself is
* <code>null</code>.
*/
public static String getMimeType(Individual fileSurrogate) {
if (fileSurrogate == null) {
return null;
}
String mimeType = fileSurrogate
.getDataValue(VitroVocabulary.FS_MIME_TYPE);
if (mimeType == null) {
log.error("File had no mimeType: '" + fileSurrogate.getURI() + "'");
} else {
log.debug("mimeType for '" + fileSurrogate.getURI() + "' was '"
+ mimeType + "'");
}
return mimeType;
}
/**
* Return the URI for this individual, or <code>null</code> if the
* individual is <code>null</code>.
*/
private static String getUri(Individual entity) {
if (entity == null) {
return null;
} else {
return entity.getURI();
}
}
/**
* Locate the URI of the bytestream of the main image for this entity.
*
* @return the URI, or <code>null</code> if there is no such bytestream, or
* if the entity itself is <code>null</code>.
*/
public static String getMainImageBytestreamUri(Individual entity) {
Individual mainFile = getMainImage(entity);
Individual byteStream = getBytestreamForFile(mainFile);
return getUri(byteStream);
}
/**
* Find the filename of the main image for this entity.
*
* @return the filename, or <code>null</code> if there is no such image.
*/
public static String getMainImageFilename(Individual entity) {
Individual mainFile = getMainImage(entity);
return getFilename(mainFile);
}
/**
* Locate the individual that represents the bytestream of the thumbnail of
* the main image for this entity.
*
* @return the URI, or <code>null</code> if there is no such thumbnail
* image, or if the entity itself is <code>null</code>.
*/
public static String getThumbnailBytestreamUri(Individual entity) {
Individual mainFile = getMainImage(entity);
Individual thumbFile = getThumbnailForImage(mainFile);
Individual byteStream = getBytestreamForFile(thumbFile);
return getUri(byteStream);
}
/**
* Find the filename of the thumbnail of the main image.
*
* @return the filename, or <code>null</code> if there is no such thumbnail
* image, or if the entity itself is <code>null</code>.
*/
public static String getThumbnailFilename(Individual entity) {
Individual mainFile = getMainImage(entity);
Individual thumbFile = getThumbnailForImage(mainFile);
return getFilename(thumbFile);
}
// ----------------------------------------------------------------------
// Instance methods -- need access to a WebappDaoFactory
// ----------------------------------------------------------------------
private final IndividualDao individualDao;
private final ObjectPropertyStatementDao objectPropertyStatementDao;
private final DataPropertyStatementDao dataPropertyStatementDao;
public FileModelHelper(WebappDaoFactory webappDaoFactory) {
this.individualDao = webappDaoFactory.getIndividualDao();
this.objectPropertyStatementDao = webappDaoFactory
.getObjectPropertyStatementDao();
this.dataPropertyStatementDao = webappDaoFactory
.getDataPropertyStatementDao();
}
/**
* Some of these methods require an Individual as an argument.
*/
public Individual getIndividualByUri(String uri) {
return individualDao.getIndividualByURI(uri);
}
/**
* If this URI represents a ByteStream object, we need to find it's
* surrogate object in order to find the mime type.
*
* @return the mime type, or <code>null</code> if we couldn't find the mime
* type, or if the bytestream object itself is null.
*/
public String getMimeTypeForBytestream(String bytestreamUri) {
if (bytestreamUri == null) {
return null;
}
ObjectPropertyStatement opStmt = new ObjectPropertyStatementImpl(null,
VitroVocabulary.FS_DOWNLOAD_LOCATION, bytestreamUri);
List<ObjectPropertyStatement> stmts = objectPropertyStatementDao
.getObjectPropertyStatements(opStmt);
if (stmts.size() > 1) {
String uris = "";
for (ObjectPropertyStatement stmt : stmts) {
uris += "'" + stmt.getSubjectURI() + "' ";
}
log.warn("Found " + stmts.size() + " Individuals that claim '"
+ bytestreamUri + "' as its bytestream:" + uris);
}
if (stmts.isEmpty()) {
log.warn("No individual claims '" + "' as its bytestream.");
return null;
}
Individual surrogate = individualDao.getIndividualByURI(stmts.get(0)
.getSubjectURI());
return getMimeType(surrogate);
}
/**
* Create a file surrogate individual in the model.
*/
public Individual createFileIndividual(String mimeType, String filename,
Individual byteStream) {
Individual file = new IndividualImpl();
file.setVClassURI(VitroVocabulary.FS_FILE_CLASS);
String uri = null;
try {
uri = individualDao.insertNewIndividual(file);
} catch (InsertException e) {
throw new IllegalStateException(
"Failed to create the file individual.", e);
}
dataPropertyStatementDao
.insertNewDataPropertyStatement(new DataPropertyStatementImpl(
uri, VitroVocabulary.FS_FILENAME, filename));
dataPropertyStatementDao
.insertNewDataPropertyStatement(new DataPropertyStatementImpl(
uri, VitroVocabulary.FS_MIME_TYPE, mimeType));
objectPropertyStatementDao
.insertNewObjectPropertyStatement(new ObjectPropertyStatementImpl(
uri, VitroVocabulary.FS_DOWNLOAD_LOCATION, byteStream
.getURI()));
return individualDao.getIndividualByURI(uri);
}
/**
* Create a bytestream individual in the model.
*/
public Individual createByteStreamIndividual() {
Individual byteStream = new IndividualImpl();
byteStream.setVClassURI(VitroVocabulary.FS_BYTESTREAM_CLASS);
String uri = null;
try {
uri = individualDao.insertNewIndividual(byteStream);
} catch (InsertException e) {
throw new IllegalStateException(
"Failed to create the bytestream individual.", e);
}
return individualDao.getIndividualByURI(uri);
}
/**
* Store this file surrogate as the main image on this entity.
*/
public void setAsMainImageOnEntity(Individual person,
Individual imageSurrogate) {
person.setMainImageUri(imageSurrogate.getURI());
individualDao.updateIndividual(person);
log.debug("Set main image '" + getUri(imageSurrogate) + "' on '"
+ person.getURI() + "'");
}
/**
* Remove the current main image from this entity.
*
* @return the file surrogate, or <code>null</code> if there was none.
*/
public Individual removeMainImage(Individual person) {
Individual mainImage = getMainImage(person);
person.setMainImageUri(null);
individualDao.updateIndividual(person);
log.debug("Removed main image '" + getUri(mainImage) + "' from '"
+ person.getURI() + "'");
return mainImage;
}
/**
* Remove the current thumbnail from this entity;
*
* @return the file surrogate, or <code>null</code> if there was none.
*/
public Individual removeThumbnail(Individual person) {
Individual mainImage = getMainImage(person);
Individual thumbnail = getThumbnailForImage(mainImage);
if (thumbnail == null) {
log.debug("No thumbnail to remove from '" + person.getURI() + "'");
return null;
}
ObjectPropertyStatement stmt = new ObjectPropertyStatementImpl(
mainImage.getURI(), VitroVocabulary.FS_THUMBNAIL_IMAGE,
thumbnail.getURI());
objectPropertyStatementDao.deleteObjectPropertyStatement(stmt);
return thumbnail;
}
/**
* Store this file surrogate as the thumbnail on this entity.
*/
public void setThumbnailOnIndividual(Individual entity,
Individual thumbnailSurrogate) {
String mainImageUri = entity.getMainImageUri();
objectPropertyStatementDao
.insertNewObjectPropertyStatement(new ObjectPropertyStatementImpl(
mainImageUri, VitroVocabulary.FS_THUMBNAIL_IMAGE,
thumbnailSurrogate.getURI()));
}
/**
* Are there any ObjectPropertyStatements in the model whose object is this
* file surrogate?
*/
public boolean isFileReferenced(Individual surrogate) {
if (surrogate == null) {
return false;
}
ObjectPropertyStatement opStmt = new ObjectPropertyStatementImpl(null,
null, surrogate.getURI());
List<ObjectPropertyStatement> stmts = objectPropertyStatementDao
.getObjectPropertyStatements(opStmt);
if (log.isDebugEnabled()) {
log.debug(stmts.size() + " statements referencing '"
+ surrogate.getURI() + "'");
for (ObjectPropertyStatement stmt : stmts) {
log.debug("'" + stmt.getSubjectURI() + "' -- '"
+ stmt.getPropertyURI() + "' -- '"
+ stmt.getObjectURI() + "'");
}
}
return !stmts.isEmpty();
}
/**
* This file is being deleted; remove both the surrogate and its bytestream
* from the model.
*/
public void removeFileFromModel(Individual surrogate) {
Individual bytestream = getBytestreamForFile(surrogate);
if (bytestream != null) {
individualDao.deleteIndividual(bytestream);
}
if (surrogate != null) {
individualDao.deleteIndividual(surrogate);
}
}
}

View file

@ -0,0 +1,247 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.IndividualImpl;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatementImpl;
import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.InsertException;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
/**
* A helper object to handle the mundane details of dealing with uploaded files
* in the model.
*/
public class UploadedFileHelper {
private static final Log log = LogFactory.getLog(UploadedFileHelper.class);
private final FileStorage fileStorage;
private final WebappDaoFactory wadf;
private final IndividualDao individualDao;
private final DataPropertyStatementDao dataPropertyStatementDao;
private final ObjectPropertyStatementDao objectPropertyStatementDao;
public UploadedFileHelper(FileStorage fileStorage, WebappDaoFactory wadf) {
this.fileStorage = fileStorage;
this.wadf = wadf;
this.individualDao = wadf.getIndividualDao();
this.dataPropertyStatementDao = wadf.getDataPropertyStatementDao();
this.objectPropertyStatementDao = wadf.getObjectPropertyStatementDao();
}
/**
* We have a filename, a mimetype, and some content. Create a file in the
* file storage system and in the model.
*
* @return information about the newly created file.
*/
public FileInfo createFile(String filename, String mimeType,
InputStream inputStream) throws FileAlreadyExistsException,
IOException {
if (filename == null) {
throw new NullPointerException("filename may not be null.");
}
if (mimeType == null) {
throw new NullPointerException("mimeType may not be null.");
}
if (inputStream == null) {
throw new NullPointerException("inputStream may not be null.");
}
// Create the file individuals in the model
Individual byteStream = createByteStreamIndividual();
String bytestreamUri = byteStream.getURI();
Individual file = createFileIndividual(mimeType, filename, byteStream);
String fileUri = file.getURI();
// Store the file in the FileStorage system.
fileStorage.createFile(bytestreamUri, filename, inputStream);
// Figure out the alias URL
String aliasUrl = FileServingHelper.getBytestreamAliasUrl(
bytestreamUri, filename);
// And wrap it all up in a tidy little package.
return new FileInfo.Builder().setFilename(filename)
.setMimeType(mimeType).setUri(fileUri)
.setBytestreamUri(bytestreamUri)
.setBytestreamAliasUrl(aliasUrl).build();
}
/**
* Record this image file and thumbnail on this entity.
*/
public void setImagesOnEntity(String entityUri, FileInfo mainInfo,
FileInfo thumbInfo) {
if (entityUri == null) {
throw new NullPointerException("entityUri may not be null.");
}
if (mainInfo == null) {
throw new NullPointerException("mainInfo may not be null.");
}
if (thumbInfo == null) {
throw new NullPointerException("thumbInfo may not be null.");
}
Individual entity = individualDao.getIndividualByURI(entityUri);
if (entity == null) {
throw new NullPointerException("No entity found for URI '"
+ entityUri + "'.");
}
// Add the thumbnail file to the main image file.
objectPropertyStatementDao
.insertNewObjectPropertyStatement(new ObjectPropertyStatementImpl(
mainInfo.getUri(), VitroVocabulary.FS_THUMBNAIL_IMAGE,
thumbInfo.getUri()));
// Add the main image file to the entity.
entity.setMainImageUri(mainInfo.getUri());
individualDao.updateIndividual(entity);
log.debug("Set images on '" + entity.getURI() + "': main=" + mainInfo
+ ", thumb=" + thumbInfo);
}
/**
* If this Individual has an image, remove it and the thumbnail. If the
* image file and/or the thumbnail file have no other references, delete
* them.
*
* Note: after this operation, entity is stale.
*/
public void removeMainImage(Individual entity) {
ImageInfo imageInfo = ImageInfo.instanceFromEntityUri(wadf, entity);
if (imageInfo == null) {
log.debug("No image to remove from '" + entity.getURI() + "'");
return;
}
// Remove the main image from the entity.
entity.setMainImageUri(null);
individualDao.updateIndividual(entity);
// Remove the thumbnail from the main image.
ObjectPropertyStatement stmt = new ObjectPropertyStatementImpl(
imageInfo.getMainImage().getUri(),
VitroVocabulary.FS_THUMBNAIL_IMAGE, imageInfo.getThumbnail()
.getUri());
objectPropertyStatementDao.deleteObjectPropertyStatement(stmt);
// If nobody else is using them, get rid of then.
deleteIfNotReferenced(imageInfo.getMainImage());
deleteIfNotReferenced(imageInfo.getThumbnail());
}
/**
* Create a bytestream individual in the model. It's just a naked individual
* with no properties.
*/
private Individual createByteStreamIndividual() {
Individual byteStream = new IndividualImpl();
byteStream.setVClassURI(VitroVocabulary.FS_BYTESTREAM_CLASS);
String uri = null;
try {
uri = individualDao.insertNewIndividual(byteStream);
} catch (InsertException e) {
throw new IllegalStateException(
"Failed to create the bytestream individual.", e);
}
return individualDao.getIndividualByURI(uri);
}
/**
* Create a file surrogate individual in the model. It has data properties
* for filename and mimeType. It also has a link to its bytestream
* Individual.
*/
private Individual createFileIndividual(String mimeType, String filename,
Individual byteStream) {
Individual file = new IndividualImpl();
file.setVClassURI(VitroVocabulary.FS_FILE_CLASS);
String uri = null;
try {
uri = individualDao.insertNewIndividual(file);
} catch (InsertException e) {
throw new IllegalStateException(
"Failed to create the file individual.", e);
}
dataPropertyStatementDao
.insertNewDataPropertyStatement(new DataPropertyStatementImpl(
uri, VitroVocabulary.FS_FILENAME, filename));
dataPropertyStatementDao
.insertNewDataPropertyStatement(new DataPropertyStatementImpl(
uri, VitroVocabulary.FS_MIME_TYPE, mimeType));
objectPropertyStatementDao
.insertNewObjectPropertyStatement(new ObjectPropertyStatementImpl(
uri, VitroVocabulary.FS_DOWNLOAD_LOCATION, byteStream
.getURI()));
return individualDao.getIndividualByURI(uri);
}
/**
* If nobody is using this file any more, delete its bytestream from the
* file system, and delete both the surrogate and the bytestream from the
* model.
*/
private void deleteIfNotReferenced(FileInfo file) {
if (!isFileReferenced(file.getUri())) {
try {
fileStorage.deleteFile(file.getBytestreamUri());
individualDao.deleteIndividual(file.getBytestreamUri());
individualDao.deleteIndividual(file.getUri());
} catch (IOException e) {
throw new IllegalStateException("Can't delete the file: '"
+ file.getBytestreamUri(), e);
}
}
}
/**
* Are there any ObjectPropertyStatements in the model whose object is this
* file surrogate?
*/
private boolean isFileReferenced(String surrogateUri) {
if (surrogateUri == null) {
return false;
}
ObjectPropertyStatement opStmt = new ObjectPropertyStatementImpl(null,
null, surrogateUri);
List<ObjectPropertyStatement> stmts = objectPropertyStatementDao
.getObjectPropertyStatements(opStmt);
if (log.isDebugEnabled()) {
log.debug(stmts.size() + " statements referencing '" + surrogateUri
+ "'");
for (ObjectPropertyStatement stmt : stmts) {
log.debug("'" + stmt.getSubjectURI() + "' -- '"
+ stmt.getPropertyURI() + "' -- '"
+ stmt.getObjectURI() + "'");
}
}
return !stmts.isEmpty();
}
}

View file

@ -2,11 +2,162 @@
package edu.cornell.mannlib.vitro.webapp.filestorage.model;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatementImpl;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
/**
* An immutable packet of information about an uploaded file, with a builder
* class to permit incremental construction.
*/
public class FileInfo {
private static final Log log = LogFactory.getLog(FileInfo.class);
// ----------------------------------------------------------------------
// static Factory methods.
// ----------------------------------------------------------------------
/**
* If this request URL represents a BytestreamAliasURL, find the bytestream
* URI, find the surrogate, and get the info. Otherwise, return null.
*/
public static FileInfo instanceFromAliasUrl(
WebappDaoFactory webappDaoFactory, String path) {
String bytestreamUri = FileServingHelper.getBytestreamUri(path);
if (bytestreamUri == null) {
return null;
}
return instanceFromBytestreamUri(webappDaoFactory, bytestreamUri);
}
/**
* If this URI represents a file bytestream, find its surrogate and get its
* info. Otherwise, return null.
*/
public static FileInfo instanceFromBytestreamUri(
WebappDaoFactory webappDaoFactory, String bytestreamUri) {
IndividualDao individualDao = webappDaoFactory.getIndividualDao();
Individual entity = individualDao.getIndividualByURI(bytestreamUri);
if (!isFileBytestream(entity)) {
return null;
}
ObjectPropertyStatementDao objectPropertyStatementDao = webappDaoFactory
.getObjectPropertyStatementDao();
ObjectPropertyStatement opStmt = new ObjectPropertyStatementImpl(null,
VitroVocabulary.FS_DOWNLOAD_LOCATION, entity.getURI());
List<ObjectPropertyStatement> stmts = objectPropertyStatementDao
.getObjectPropertyStatements(opStmt);
if (stmts.size() > 1) {
String uris = "";
for (ObjectPropertyStatement stmt : stmts) {
uris += "'" + stmt.getSubjectURI() + "' ";
}
log.warn("Found " + stmts.size() + " Individuals that claim '"
+ entity.getURI() + "' as its bytestream:" + uris);
}
if (stmts.isEmpty()) {
log.warn("No individual claims '" + entity.getURI()
+ "' as its bytestream.");
return null;
}
String surrogateUri = stmts.get(0).getSubjectURI();
return instanceFromSurrogateUri(webappDaoFactory, surrogateUri);
}
/**
* If this URI represents a file surrogate, get its info. Otherwise, return
* null.
*/
public static FileInfo instanceFromSurrogateUri(
WebappDaoFactory webappDaoFactory, String uri) {
IndividualDao individualDao = webappDaoFactory.getIndividualDao();
Individual surrogate = individualDao.getIndividualByURI(uri);
if (!isFileSurrogate(surrogate)) {
return null;
}
String filename = surrogate.getDataValue(VitroVocabulary.FS_FILENAME);
if (filename == null) {
log.error("File had no filename: '" + uri + "'");
} else {
log.debug("Filename for '" + uri + "' was '" + filename + "'");
}
String mimeType = surrogate.getDataValue(VitroVocabulary.FS_MIME_TYPE);
if (mimeType == null) {
log.error("File had no mimeType: '" + uri + "'");
} else {
log.debug("mimeType for '" + uri + "' was '" + mimeType + "'");
}
String bytestreamUri;
Individual byteStream = surrogate
.getRelatedIndividual(VitroVocabulary.FS_DOWNLOAD_LOCATION);
if (byteStream == null) {
bytestreamUri = null;
log.error("File surrogate '" + uri
+ "' had no associated bytestream.");
} else {
bytestreamUri = byteStream.getURI();
log.debug("File surroage'" + uri + "' had associated bytestream: '"
+ byteStream.getURI() + "'");
}
String bytestreamAliasUrl = FileServingHelper.getBytestreamAliasUrl(
bytestreamUri, filename);
return new FileInfo.Builder().setUri(uri).setFilename(filename)
.setMimeType(mimeType).setBytestreamUri(bytestreamUri)
.setBytestreamAliasUrl(bytestreamAliasUrl).build();
}
/**
* Is this a FileByteStream individual?
*/
private static boolean isFileBytestream(Individual entity) {
if (entity == null) {
return false;
}
if (entity.isVClass(VitroVocabulary.FS_BYTESTREAM_CLASS)) {
log.debug("Entity '" + entity.getURI() + "' is a bytestream");
return true;
}
log.debug("Entity '" + entity.getURI() + "' is not a bytestream");
return false;
}
/**
* Is this a File individual?
*/
private static boolean isFileSurrogate(Individual entity) {
if (entity == null) {
return false;
}
if (entity.isVClass(VitroVocabulary.FS_FILE_CLASS)) {
log.debug("Entity '" + entity.getURI() + "' is a file surrogate");
return true;
}
log.debug("Entity '" + entity.getURI() + "' is not a file surrogate");
return false;
}
// ----------------------------------------------------------------------
// The instance variables and methods.
// ----------------------------------------------------------------------
private final String uri;
private final String filename;
private final String mimeType;
@ -48,6 +199,10 @@ public class FileInfo {
+ bytestreamAliasUrl + "]";
}
// ----------------------------------------------------------------------
// The builder.
// ----------------------------------------------------------------------
/**
* A builder class allows us to supply the values one at a time, and then
* freeze them into an immutable object.
@ -88,4 +243,5 @@ public class FileInfo {
return new FileInfo(this);
}
}
}

View file

@ -0,0 +1,127 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.model;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
/**
* An immutable packet of information about an iamge file and its thumbnail.
*/
public class ImageInfo {
private static final Log log = LogFactory.getLog(ImageInfo.class);
public static final ImageInfo EMPTY_IMAGE_INFO = emptyInstance();
// ----------------------------------------------------------------------
// static Factory methods.
// ----------------------------------------------------------------------
/**
* This is a place holder for an Individual with no image.
*/
private static ImageInfo emptyInstance() {
FileInfo emptyFileInfo = new FileInfo.Builder().build();
return new ImageInfo(emptyFileInfo, emptyFileInfo);
}
/**
* If this Individual has a main image and a thumbnail, return their info.
* Otherwise, return null.
*/
public static ImageInfo instanceFromEntityUri(
WebappDaoFactory webappDaoFactory, Individual entity) {
if (webappDaoFactory == null) {
throw new NullPointerException("webappDaoFactory may not be null.");
}
if (entity == null) {
return null;
}
IndividualDao individualDao = webappDaoFactory.getIndividualDao();
String mainImageUri = entity.getMainImageUri();
if (mainImageUri == null) {
log.debug("Entity '" + entity.getURI()
+ "' had no associated main image.");
return null;
} else {
log.debug("Entity '" + entity.getURI()
+ "' had associated main image: '" + mainImageUri + "'");
}
Individual mainFile = individualDao.getIndividualByURI(mainImageUri);
if (mainFile == null) {
log.error("Entity '" + entity.getURI()
+ "' has a main image URI that does not refer to an "
+ "individual: mainImageURI=" + mainImageUri);
return null;
}
Individual thumbFile = mainFile
.getRelatedIndividual(VitroVocabulary.FS_THUMBNAIL_IMAGE);
if (thumbFile == null) {
log.warn("Main image file '" + mainImageUri
+ "' had no associated thumbnail.");
return null;
} else {
log.debug("Main image file '" + mainImageUri
+ "' had associated thumbnail: '" + thumbFile.getURI()
+ "'");
}
FileInfo mainInfo = FileInfo.instanceFromSurrogateUri(webappDaoFactory,
mainFile.getURI());
if (mainInfo == null) {
log.error("Entity '" + entity.getURI()
+ "' has a mainImage that is not a File "
+ "surrogate: mainImageURI=" + mainImageUri);
return null;
}
FileInfo thumbInfo = FileInfo.instanceFromSurrogateUri(
webappDaoFactory, thumbFile.getURI());
if (thumbInfo == null) {
log.error("Entity '" + entity.getURI()
+ "' has a mainImage with a thumbnail that is not a File "
+ "surrogate: mainImageURI=" + mainImageUri
+ ", thumbnailURI=" + thumbFile.getURI());
return null;
}
return new ImageInfo(mainInfo, thumbInfo);
}
// ----------------------------------------------------------------------
// instance fields and methods.
// ----------------------------------------------------------------------
private final FileInfo mainImage;
private final FileInfo thumbnail;
public ImageInfo(FileInfo mainImage, FileInfo thumbnail) {
if (mainImage == null) {
throw new NullPointerException("mainImage may not be null.");
}
if (thumbnail == null) {
throw new NullPointerException("thumbnail may not be null.");
}
this.mainImage = mainImage;
this.thumbnail = thumbnail;
}
public FileInfo getMainImage() {
return mainImage;
}
public FileInfo getThumbnail() {
return thumbnail;
}
}

View file

@ -23,10 +23,9 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
/**
* <p>
@ -79,9 +78,10 @@ public class FileServingServlet extends VitroHttpServlet {
String path = request.getServletPath() + request.getPathInfo();
log.debug("Path is '" + path + "'");
String uri = FileServingHelper.getBytestreamUri(path);
log.debug("Bytestream URI is '" + uri + "'");
if (uri == null) {
FileInfo fileInfo = FileInfo.instanceFromAliasUrl(request
.getFullWebappDaoFactory(), path);
log.debug("File info is '" + fileInfo + "'");
if (fileInfo == null) {
String message = "The request path is not valid for the File servlet: '"
+ path + "'";
log.error(message);
@ -91,12 +91,7 @@ public class FileServingServlet extends VitroHttpServlet {
// Validate that the file exists, with the requested URI and filename.
String requestedFilename = getFilename(path);
String actualFilename = fileStorage.getFilename(uri);
if (actualFilename == null) {
log.debug("Requested a non-existent file: " + path);
response.sendError(SC_NOT_FOUND, ("File not found: " + path));
return;
}
String actualFilename = fileInfo.getFilename();
if (!actualFilename.equals(requestedFilename)
&& !actualFilename.equals(decode(requestedFilename))) {
log.warn("The requested filename does not match the "
@ -107,13 +102,12 @@ public class FileServingServlet extends VitroHttpServlet {
}
// Get the MIME type.
String mimeType = new FileModelHelper(request.getFullWebappDaoFactory())
.getMimeTypeForBytestream(uri);
String mimeType = fileInfo.getMimeType();
// Open the actual byte stream.
InputStream in;
try {
in = fileStorage.getInputStream(uri, actualFilename);
in = fileStorage.getInputStream(fileInfo.getBytestreamUri(), actualFilename);
} catch (FileNotFoundException e) {
log.error(e, e);
response.sendError(SC_INTERNAL_SERVER_ERROR, e.toString());

View file

@ -6,7 +6,7 @@ import java.io.File;
import com.hp.hpl.jena.rdf.model.Model;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
/**
@ -23,12 +23,12 @@ public interface FSUController {
/** 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();
/** A file model helper with access to the DAO layer. */
FileModelHelper getFileModelHelper();
/** Where to store the files that were translated. */
File getTranslatedDirectory();

View file

@ -13,7 +13,7 @@ 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.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
/**
@ -104,7 +104,7 @@ public class FileStorageUpdater implements FSUController {
private final Model model;
private final FileStorage fileStorage;
private final FileModelHelper fileModelHelper;
private final UploadedFileHelper uploadedFileHelper;
private final ImageDirectoryWithBackup imageDirectoryWithBackup;
private final File upgradeDirectory;
@ -115,7 +115,7 @@ public class FileStorageUpdater implements FSUController {
File webappImageDirectory) {
this.model = model;
this.fileStorage = fileStorage;
this.fileModelHelper = new FileModelHelper(wadf);
this.uploadedFileHelper = new UploadedFileHelper(fileStorage, wadf);
this.upgradeDirectory = new File(uploadDirectory, "upgrade");
this.imageDirectoryWithBackup = new ImageDirectoryWithBackup(new File(
@ -201,7 +201,7 @@ public class FileStorageUpdater implements FSUController {
model.createProperty(IMAGETHUMB)).isEmpty()) {
return true;
}
return false;
}
@ -238,8 +238,8 @@ public class FileStorageUpdater implements FSUController {
}
@Override
public FileModelHelper getFileModelHelper() {
return this.fileModelHelper;
public UploadedFileHelper getUploadedFileHelper() {
return this.uploadedFileHelper;
}
@Override

View file

@ -16,9 +16,9 @@ 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.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
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,
@ -27,15 +27,15 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
*/
public class ImageSchemaTranslater extends FsuScanner {
private final ImageDirectoryWithBackup imageDirectoryWithBackup;
protected final FileModelHelper fileModelHelper;
protected final FileStorage fileStorage;
protected final UploadedFileHelper uploadedFileHelper;
public ImageSchemaTranslater(FSUController controller) {
super(controller);
this.imageDirectoryWithBackup = controller
.getImageDirectoryWithBackup();
this.fileStorage = controller.getFileStorage();
this.fileModelHelper = controller.getFileModelHelper();
this.uploadedFileHelper = controller.getUploadedFileHelper();
}
/**
@ -79,10 +79,6 @@ public class ImageSchemaTranslater extends FsuScanner {
return;
}
translateMainImage(resource, mainImages.get(0));
translated.add(mainImages.get(0));
ResourceWrapper.removeAll(resource, imageProperty);
List<String> thumbnails = getValues(resource, thumbProperty);
if (thumbnails.size() != 1) {
updateLog.error(resource, "has " + thumbnails.size()
@ -90,63 +86,47 @@ public class ImageSchemaTranslater extends FsuScanner {
return;
}
translateThumbnail(resource, thumbnails.get(0));
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 the main image into the new system
*/
private void translateMainImage(Resource resource, String path) {
Individual file = translateFile(resource, path, "main image");
Individual person = fileModelHelper.getIndividualByUri(resource
.getURI());
fileModelHelper.setAsMainImageOnEntity(person, file);
}
/**
* Translate the thumbnail into the new system.
*/
private void translateThumbnail(Resource resource, String path) {
Individual file = translateFile(resource, path, "thumbnail");
Individual person = fileModelHelper.getIndividualByUri(resource
.getURI());
fileModelHelper.setThumbnailOnIndividual(person, file);
}
/**
* Translate an image file into the new system
* <ul>
* <li>Create a new File, FileByteStream.</li>
* <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>
*
* @return the new File surrogate.
*/
private Individual translateFile(Resource resource, String path,
String label) {
private FileInfo translateFile(Resource resource, String path, String label) {
File oldFile = imageDirectoryWithBackup.getExistingFile(path);
String filename = getSimpleFilename(path);
String mimeType = guessMimeType(resource, filename);
// Create the file individuals in the model
Individual byteStream = fileModelHelper.createByteStreamIndividual();
Individual file = fileModelHelper.createFileIndividual(mimeType,
filename, byteStream);
updateLog.log(resource, "translating " + label + " '" + path
+ "' into the file storage as '" + file.getURI() + "'");
InputStream inputStream = null;
try {
// Store the file in the FileStorage system.
inputStream = new FileInputStream(oldFile);
fileStorage.createFile(byteStream.getURI(), filename, inputStream);
// 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 {
@ -156,8 +136,6 @@ public class ImageSchemaTranslater extends FsuScanner {
}
}
}
return file;
}
/**

View file

@ -43,9 +43,10 @@ import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatementImpl;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.RdfLiteralHash;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.model.ImageInfo;
import edu.cornell.mannlib.vitro.webapp.utils.FrontEndEditingUtils;
import edu.cornell.mannlib.vitro.webapp.utils.StringUtils;
@ -470,19 +471,22 @@ public class PropertyEditLinks extends TagSupport{
protected LinkStruct[] doImageLinks(Individual entity,
IdentifierBundle ids, PolicyIface policy, String contextPath) {
Individual mainImage = FileModelHelper.getMainImage(entity);
Individual thumbnail = FileModelHelper.getThumbnailForImage(mainImage);
VitroRequest vreq = new VitroRequest((HttpServletRequest) pageContext
.getRequest());
ImageInfo imageInfo = ImageInfo.instanceFromEntityUri(vreq
.getFullWebappDaoFactory(), entity);
String subjectUri = entity.getURI();
String predicateUri = VitroVocabulary.IND_MAIN_IMAGE;
if (thumbnail == null) {
if (imageInfo == null) {
EditLinkAccess[] accesses = policyToAccess(ids, policy, subjectUri,
predicateUri);
if (contains(accesses, EditLinkAccess.ADDNEW)) {
log.debug("permission to ADD main image to " + subjectUri);
boolean isPerson = entity.isVClass("http://xmlns.com/foaf/0.1/Person");
boolean isPerson = entity
.isVClass("http://xmlns.com/foaf/0.1/Person");
if (isPerson) {
return new LinkStruct[] { getImageLink(subjectUri,
contextPath, "edit", "upload an image", "add") };
@ -496,7 +500,7 @@ public class PropertyEditLinks extends TagSupport{
}
} else {
ObjectPropertyStatement prop = new ObjectPropertyStatementImpl(
subjectUri, predicateUri, mainImage.getURI());
subjectUri, predicateUri, imageInfo.getMainImage().getUri());
EditLinkAccess[] allowedAccessTypeArray = policyToAccess(ids,
policy, prop);
@ -520,8 +524,7 @@ public class PropertyEditLinks extends TagSupport{
}
return links.toArray(new LinkStruct[links.size()]);
}
}
}
/* ********************* utility methods ********************************* */
protected static String makeRelativeHref( String baseUrl, String ... queries ) {