NIHVIVO-157 manual merge of the changes from branch NIHVIVO-157-file-storage

This commit is contained in:
jeb228 2010-06-20 19:23:19 +00:00
parent 083aa4e530
commit fb7d5bbba9
51 changed files with 3535 additions and 1198 deletions

View file

@ -109,8 +109,7 @@ public class CuratorEditingPolicy implements VisitingPolicyIface {
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
this.editableVitroUris.add(VitroVocabulary.CITATION);
this.editableVitroUris.add(VitroVocabulary.IMAGEFILE);
this.editableVitroUris.add(VitroVocabulary.IMAGETHUMB);
this.editableVitroUris.add(VitroVocabulary.IND_MAIN_IMAGE);
this.editableVitroUris.add(VitroVocabulary.LINK);
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);

View file

@ -112,8 +112,7 @@ public class DbAdminEditingPolicy implements VisitingPolicyIface {
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
this.editableVitroUris.add(VitroVocabulary.CITATION);
this.editableVitroUris.add(VitroVocabulary.IMAGEFILE);
this.editableVitroUris.add(VitroVocabulary.IMAGETHUMB);
this.editableVitroUris.add(VitroVocabulary.IND_MAIN_IMAGE);
this.editableVitroUris.add(VitroVocabulary.LINK);
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);

View file

@ -110,8 +110,7 @@ public class EditorEditingPolicy implements VisitingPolicyIface{
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
this.editableVitroUris.add(VitroVocabulary.CITATION);
this.editableVitroUris.add(VitroVocabulary.IMAGEFILE);
this.editableVitroUris.add(VitroVocabulary.IMAGETHUMB);
this.editableVitroUris.add(VitroVocabulary.IND_MAIN_IMAGE);
this.editableVitroUris.add(VitroVocabulary.LINK);
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);

View file

@ -5,8 +5,6 @@ package edu.cornell.mannlib.vitro.webapp.auth.policy;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -110,8 +108,7 @@ public class SelfEditingPolicy implements VisitingPolicyIface {
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
this.editableVitroUris.add(VitroVocabulary.CITATION);
this.editableVitroUris.add(VitroVocabulary.IMAGEFILE);
this.editableVitroUris.add(VitroVocabulary.IMAGETHUMB);
this.editableVitroUris.add(VitroVocabulary.IND_MAIN_IMAGE);
this.editableVitroUris.add(VitroVocabulary.LINK);
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);

View file

@ -93,11 +93,11 @@ public interface Individual extends ResourceBean, VitroTimeWindowedResource, Com
String getStatus();
void setStatus(String s);
String getImageFile();
void setImageFile(String imageFile);
String getImageThumb();
void setImageThumb(String imageThumb);
void setMainImageUri(String mainImageUri);
String getMainImageUri();
String getImageUrl();
String getThumbUrl();
String getUrl();
void setUrl(String url);

View file

@ -1,132 +1,141 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.beans;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.Collator;
import java.util.*;
/**
* Represents a single entity record.
*/
public class IndividualImpl extends BaseResourceBean implements Individual, Comparable<Individual> {
public String name = null;
public String vClassURI = null;
protected VClass vClass = null;
protected List<VClass> directVClasses = null;
protected List<VClass> allVClasses = null;
protected Date sunrise = null;
protected Date sunset = null;
protected Date timekey = null;
protected Timestamp modTime = null;
protected List <ObjectProperty>propertyList = null;
protected Map <String,ObjectProperty> objectPropertyMap = null;
protected List <DataProperty>datatypePropertyList = null;
protected Map <String,DataProperty> dataPropertyMap = null;
protected List <DataPropertyStatement>dataPropertyStatements = null;
protected List <ObjectPropertyStatement>objectPropertyStatements = null;
protected List <ObjectPropertyStatement>rangeEnts2Ents = null;
protected List <DataPropertyStatement>externalIds = null;
protected String moniker = null;
protected String url = null;
protected String description = null;
protected String imageFile = null;
protected String anchor = null;
protected String blurb = null;
protected String imageThumb = null;
protected String citation = null;
protected int statusId = 0;
protected String status = null;
protected List <Link>linksList = null;
protected Link primaryLink = null;
protected List<String> keywords=null;
protected List<Keyword> keywordObjects=null;
protected Float searchBoost;
/** indicates if sortForDisplay has been called */
protected boolean sorted = false;
protected boolean DIRECT = true;
protected boolean ALL = false;
public IndividualImpl() {
}
public IndividualImpl(String URI) {
this.setURI(URI);
this.setVClasses(new ArrayList<VClass>(), DIRECT);
this.setVClasses(new ArrayList<VClass>(), ALL);
this.setObjectPropertyStatements(new ArrayList<ObjectPropertyStatement>());
this.setObjectPropertyMap(new HashMap<String, ObjectProperty>());
this.setDataPropertyStatements(new ArrayList<DataPropertyStatement>());
this.setDataPropertyMap(new HashMap<String, DataProperty>());
this.setPropertyList(new ArrayList<ObjectProperty>());
this.setDatatypePropertyList(new ArrayList<DataProperty>());
}
public String getName(){return name;}
public void setName(String in){name=in;}
// private String modTime = null;
// public String getModtime(){return modTime;}
// public void setModtime(String in){modTime=in;}
public String getVClassURI(){return vClassURI;}
public void setVClassURI(String in){vClassURI=in;}
public Date getSunrise(){return sunrise;}
public void setSunrise(Date in){sunrise=in;}
public Date getSunset(){return sunset;}
public void setSunset(Date in){sunset=in;}
public Date getTimekey(){return timekey;}
public void setTimekey(Date in){timekey=in;}
/**
* Returns the last time this object was changed in the model.
* Notice Java API craziness: Timestamp is a subclass of Date
* but there are notes in the Javadoc that you should not pretend
* that a Timestamp is a Date. (Crazy ya?) In particular,
* Timestamp.equals(Date) will never return true because of
* the 'nanos.'
*/
public Timestamp getModTime(){return modTime;}
public void setModTime(Timestamp in){modTime=in;}
public List<ObjectProperty> getObjectPropertyList() {
return propertyList;
}
public void setPropertyList(List <ObjectProperty>propertyList) {
this.propertyList = propertyList;
}
public Map<String,ObjectProperty> getObjectPropertyMap() {
return this.objectPropertyMap;
}
public void setObjectPropertyMap( Map<String,ObjectProperty> propertyMap ) {
this.objectPropertyMap = propertyMap;
}
public List <DataProperty>getDataPropertyList() {
return datatypePropertyList;
}
public void setDatatypePropertyList(List <DataProperty>datatypePropertyList) {
this.datatypePropertyList = datatypePropertyList;
}
public Map<String,DataProperty> getDataPropertyMap() {
return this.dataPropertyMap;
}
public void setDataPropertyMap( Map<String,DataProperty> propertyMap ) {
this.dataPropertyMap = propertyMap;
}
public void setDataPropertyStatements(List <DataPropertyStatement>list) {
dataPropertyStatements = list;
}
public List<DataPropertyStatement> getDataPropertyStatements(){
return dataPropertyStatements;
package edu.cornell.mannlib.vitro.webapp.beans;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.Collator;
import java.util.*;
/**
* Represents a single entity record.
*/
public class IndividualImpl extends BaseResourceBean implements Individual, Comparable<Individual> {
/**
* This can be used as a "not initialized" indicator for a property that
* could validly be set to <code>null</code>. If <code>get()</code> is
* called on such a property, and the property has this value, the correct
* value can be fetched and cached.
*/
protected static final String NOT_INITIALIZED = "__%NOT_INITIALIZED%__";
public String name = null;
public String vClassURI = null;
protected VClass vClass = null;
protected List<VClass> directVClasses = null;
protected List<VClass> allVClasses = null;
protected Date sunrise = null;
protected Date sunset = null;
protected Date timekey = null;
protected Timestamp modTime = null;
protected List <ObjectProperty>propertyList = null;
protected Map <String,ObjectProperty> objectPropertyMap = null;
protected List <DataProperty>datatypePropertyList = null;
protected Map <String,DataProperty> dataPropertyMap = null;
protected List <DataPropertyStatement>dataPropertyStatements = null;
protected List <ObjectPropertyStatement>objectPropertyStatements = null;
protected List <ObjectPropertyStatement>rangeEnts2Ents = null;
protected List <DataPropertyStatement>externalIds = null;
protected String moniker = null;
protected String url = null;
protected String description = null;
protected String anchor = null;
protected String blurb = null;
protected String mainImageUri = NOT_INITIALIZED;
protected String imageUrl;
protected String thumbUrl;
protected String citation = null;
protected int statusId = 0;
protected String status = null;
protected List <Link>linksList = null;
protected Link primaryLink = null;
protected List<String> keywords=null;
protected List<Keyword> keywordObjects=null;
protected Float searchBoost;
/** indicates if sortForDisplay has been called */
protected boolean sorted = false;
protected boolean DIRECT = true;
protected boolean ALL = false;
public IndividualImpl() {
}
public IndividualImpl(String URI) {
this.setURI(URI);
this.setVClasses(new ArrayList<VClass>(), DIRECT);
this.setVClasses(new ArrayList<VClass>(), ALL);
this.setObjectPropertyStatements(new ArrayList<ObjectPropertyStatement>());
this.setObjectPropertyMap(new HashMap<String, ObjectProperty>());
this.setDataPropertyStatements(new ArrayList<DataPropertyStatement>());
this.setDataPropertyMap(new HashMap<String, DataProperty>());
this.setPropertyList(new ArrayList<ObjectProperty>());
this.setDatatypePropertyList(new ArrayList<DataProperty>());
}
public String getName(){return name;}
public void setName(String in){name=in;}
// private String modTime = null;
// public String getModtime(){return modTime;}
// public void setModtime(String in){modTime=in;}
public String getVClassURI(){return vClassURI;}
public void setVClassURI(String in){vClassURI=in;}
public Date getSunrise(){return sunrise;}
public void setSunrise(Date in){sunrise=in;}
public Date getSunset(){return sunset;}
public void setSunset(Date in){sunset=in;}
public Date getTimekey(){return timekey;}
public void setTimekey(Date in){timekey=in;}
/**
* Returns the last time this object was changed in the model.
* Notice Java API craziness: Timestamp is a subclass of Date
* but there are notes in the Javadoc that you should not pretend
* that a Timestamp is a Date. (Crazy ya?) In particular,
* Timestamp.equals(Date) will never return true because of
* the 'nanos.'
*/
public Timestamp getModTime(){return modTime;}
public void setModTime(Timestamp in){modTime=in;}
public List<ObjectProperty> getObjectPropertyList() {
return propertyList;
}
public void setPropertyList(List <ObjectProperty>propertyList) {
this.propertyList = propertyList;
}
public Map<String,ObjectProperty> getObjectPropertyMap() {
return this.objectPropertyMap;
}
public void setObjectPropertyMap( Map<String,ObjectProperty> propertyMap ) {
this.objectPropertyMap = propertyMap;
}
public List <DataProperty>getDataPropertyList() {
return datatypePropertyList;
}
public void setDatatypePropertyList(List <DataProperty>datatypePropertyList) {
this.datatypePropertyList = datatypePropertyList;
}
public Map<String,DataProperty> getDataPropertyMap() {
return this.dataPropertyMap;
}
public void setDataPropertyMap( Map<String,DataProperty> propertyMap ) {
this.dataPropertyMap = propertyMap;
}
public void setDataPropertyStatements(List <DataPropertyStatement>list) {
dataPropertyStatements = list;
}
public List<DataPropertyStatement> getDataPropertyStatements(){
return dataPropertyStatements;
}
public List<DataPropertyStatement> getDataPropertyStatements(String propertyUri) {
@ -158,41 +167,41 @@ public class IndividualImpl extends BaseResourceBean implements Individual, Comp
public String getDataValue(String propertyUri) {
List<DataPropertyStatement> stmts = getDataPropertyStatements(propertyUri);
return stmts.isEmpty() ? null : stmts.get(0).getData();
}
public VClass getVClass() {
return vClass;
}
public void setVClass(VClass class1) {
vClass = class1;
}
public List<VClass> getVClasses() {
return allVClasses;
}
public List<VClass> getVClasses(boolean direct) {
if (direct) {
return directVClasses;
} else {
return allVClasses;
}
}
public void setVClasses(List<VClass> vClassList, boolean direct) {
if (direct) {
this.directVClasses = vClassList;
} else {
this.allVClasses = vClassList;
}
}
public void setObjectPropertyStatements(List<ObjectPropertyStatement> list) {
objectPropertyStatements = list;
}
public List <ObjectPropertyStatement> getObjectPropertyStatements(){
return objectPropertyStatements;
}
public VClass getVClass() {
return vClass;
}
public void setVClass(VClass class1) {
vClass = class1;
}
public List<VClass> getVClasses() {
return allVClasses;
}
public List<VClass> getVClasses(boolean direct) {
if (direct) {
return directVClasses;
} else {
return allVClasses;
}
}
public void setVClasses(List<VClass> vClassList, boolean direct) {
if (direct) {
this.directVClasses = vClassList;
} else {
this.allVClasses = vClassList;
}
}
public void setObjectPropertyStatements(List<ObjectPropertyStatement> list) {
objectPropertyStatements = list;
}
public List <ObjectPropertyStatement> getObjectPropertyStatements(){
return objectPropertyStatements;
}
public List<ObjectPropertyStatement> getObjectPropertyStatements(String propertyUri) {
@ -218,228 +227,239 @@ public class IndividualImpl extends BaseResourceBean implements Individual, Comp
public Individual getRelatedIndividual(String propertyUri) {
List<ObjectPropertyStatement> stmts = getObjectPropertyStatements(propertyUri);
return stmts.isEmpty() ? null : stmts.get(0).getObject();
}
public List<DataPropertyStatement> getExternalIds(){
return externalIds;
}
public void setExternalIds(List<DataPropertyStatement> externalIds){
this.externalIds = externalIds;
}
public String getMoniker(){return moniker;}
public void setMoniker(String in){moniker=in;}
public String getDescription(){return description;}
public void setDescription(String in){description=in;}
public String getAnchor(){return anchor;}
public void setAnchor(String in){anchor=in;}
public String getBlurb(){return blurb;}
public void setBlurb(String in){blurb=in;}
public String getCitation(){return citation;}
public void setCitation(String in){citation=in;}
public int getStatusId(){return statusId;}
public void setStatusId(int in){statusId=in;}
public String getStatus() {return status;}
public void setStatus(String s) {status=s; }
public String getImageFile() {
return imageFile;
}
public void setImageFile(String imageFile) {
this.imageFile = imageFile;
}
public String getImageThumb() {
return imageThumb;
}
public void setImageThumb(String imageThumb) {
this.imageThumb = imageThumb;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public List<Link> getLinksList() {
return linksList;
}
public void setLinksList(List <Link>linksList) {
this.linksList = linksList;
}
public Link getPrimaryLink() {
return primaryLink;
}
public void setPrimaryLink(Link link) {
primaryLink = link;
}
/* look at PortalFlag.numeric2numerics if you want to know which
* bits are set in a numeric flag.
*
* NOTICE:
* Values set Entity.getFlagXNumeric() will NOT be saved to the model.
*
* Also, changes to an entity flag state using Entity.setFlagXNumeric()
* are not reflected in Entity.getFlagXSet() and vice versa.
*/
protected String flag1Set = null;
public String getFlag1Set(){return flag1Set;}
public void setFlag1Set(String in){flag1Set=in;}
protected int flag1Numeric = -1;
public int getFlag1Numeric(){return flag1Numeric;}
public void setFlag1Numeric(int i){flag1Numeric=i;}
/* Consider the flagBitMask as a mask to & with flags.
if flagBitMask bit zero is set then return true if
the individual is in portal 2,
if flagBitMask bit 1 is set then return true if
the individua is in portal 4
etc.
*/
public boolean doesFlag1Match(int flagBitMask) {
return (flagBitMask & getFlag1Numeric()) != 0;
}
protected String flag2Set = null;
public String getFlag2Set(){return flag2Set;}
public void setFlag2Set(String in){flag2Set=in;}
protected int flag2Numeric = -1;
public int getFlag2Numeric(){return flag2Numeric;}
public void setFlag2Numeric(int i){flag2Numeric=i;}
protected String flag3Set = null;
public String getFlag3Set(){return flag3Set;}
public void setFlag3Set(String in){flag3Set=in;}
protected int flag3Numeric = -1;
public int getFlag3Numeric(){return flag3Numeric;}
public void setFlag3Numeric(int i){flag3Numeric=i;}
public List<String> getKeywords() { return keywords; }
public void setKeywords(List<String> keywords) {this.keywords = keywords;}
public String getKeywordString(){
String rv = "";
List keywords=getKeywords();
if (getKeywords()!=null){
Iterator<String> it1 = getKeywords().iterator();
TreeSet<String> keywordSet = new TreeSet<String>(new Comparator<String>() {
public int compare( String first, String second ) {
if (first==null) {
return 1;
}
if (second==null) {
return -1;
}
Collator collator = Collator.getInstance();
return collator.compare(first,second);
}
});
while( it1.hasNext() ){
keywordSet.add(it1.next());
}
Iterator<String> it2 = keywordSet.iterator();
while (it2.hasNext()) {
rv+= it2.next();
if( it2.hasNext())
rv+=", ";
}
}
return rv;
}
public List<Keyword> getKeywordObjects() { return keywordObjects; }
public void setKeywordObjects(List<Keyword> keywords) {this.keywordObjects = keywords;}
public Float getSearchBoost() { return searchBoost; }
public void setSearchBoost(Float boost) { searchBoost = boost; }
/**
* Sorts the ents2ents records into the proper order for display.
*
*/
public void sortForDisplay(){
if( sorted ) return;
if( getObjectPropertyList() == null ) return;
sortPropertiesForDisplay();
sortEnts2EntsForDisplay();
sorted = true;
}
protected void sortEnts2EntsForDisplay(){
if( getObjectPropertyList() == null ) return;
Iterator it = getObjectPropertyList().iterator();
while(it.hasNext()){
ObjectProperty prop = (ObjectProperty)it.next();
prop.sortObjectPropertyStatementsForDisplay(prop,prop.getObjectPropertyStatements());
}
}
protected void sortPropertiesForDisplay( ){
//here we sort the Property objects
Collections.sort(getObjectPropertyList(), new ObjectProperty.DisplayComparator());
}
public static final String [] INCLUDED_IN_JSON = {
"URI",
"name",
"moniker",
"vClassId"
};
public JSONObject toJSON() throws JSONException {
JSONObject jsonObj = new JSONObject(this, INCLUDED_IN_JSON);
return jsonObj;
}
/**
*
* @param fieldName- expected to be the field name in the format
* @return
* @throws NoSuchMethodException
*/
public Object getField(String fieldName) throws NoSuchMethodException{
if( fieldName == null || fieldName.length() == 0) return null;
if( "name".equalsIgnoreCase(fieldName) )
return getName();
if( "timekey".equalsIgnoreCase(fieldName) )
return getTimekey();
//not one of the more common ones, try reflection
//capitalize first letter
String methodName = "get" + fieldName.substring(0,1).toUpperCase()
+ fieldName.substring(1,fieldName.length());
Class cls = this.getClass();
try {
Method meth = cls.getMethod(methodName, (Class[]) null);
return meth.invoke(this,(Object[])null);
} catch (Exception e) { }
//should never get here
throw new NoSuchMethodException("Entity.getField() attempt to use a method called "
+ methodName +"() for field " + fieldName + " but the method doesn't exist.");
}
public int compareTo(Individual o2) {
Collator collator = Collator.getInstance();
if (o2 == null) {
return 1;
} else {
return collator.compare(this.getName(),o2.getName());
}
}
}
}
public List<DataPropertyStatement> getExternalIds(){
return externalIds;
}
public void setExternalIds(List<DataPropertyStatement> externalIds){
this.externalIds = externalIds;
}
public String getMoniker(){return moniker;}
public void setMoniker(String in){moniker=in;}
public String getDescription(){return description;}
public void setDescription(String in){description=in;}
public String getAnchor(){return anchor;}
public void setAnchor(String in){anchor=in;}
public String getBlurb(){return blurb;}
public void setBlurb(String in){blurb=in;}
public String getCitation(){return citation;}
public void setCitation(String in){citation=in;}
public int getStatusId(){return statusId;}
public void setStatusId(int in){statusId=in;}
public String getStatus() {return status;}
public void setStatus(String s) {status=s; }
@Override
public String getMainImageUri() {
return (mainImageUri == NOT_INITIALIZED) ? null : mainImageUri;
}
@Override
public void setMainImageUri(String mainImageUri) {
this.mainImageUri = mainImageUri;
this.imageUrl = null;
this.thumbUrl = null;
}
@Override
public String getImageUrl() {
return "imageUrl";
}
@Override
public String getThumbUrl() {
return "thumbUrl";
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public List<Link> getLinksList() {
return linksList;
}
public void setLinksList(List <Link>linksList) {
this.linksList = linksList;
}
public Link getPrimaryLink() {
return primaryLink;
}
public void setPrimaryLink(Link link) {
primaryLink = link;
}
/* look at PortalFlag.numeric2numerics if you want to know which
* bits are set in a numeric flag.
*
* NOTICE:
* Values set Entity.getFlagXNumeric() will NOT be saved to the model.
*
* Also, changes to an entity flag state using Entity.setFlagXNumeric()
* are not reflected in Entity.getFlagXSet() and vice versa.
*/
protected String flag1Set = null;
public String getFlag1Set(){return flag1Set;}
public void setFlag1Set(String in){flag1Set=in;}
protected int flag1Numeric = -1;
public int getFlag1Numeric(){return flag1Numeric;}
public void setFlag1Numeric(int i){flag1Numeric=i;}
/* Consider the flagBitMask as a mask to & with flags.
if flagBitMask bit zero is set then return true if
the individual is in portal 2,
if flagBitMask bit 1 is set then return true if
the individua is in portal 4
etc.
*/
public boolean doesFlag1Match(int flagBitMask) {
return (flagBitMask & getFlag1Numeric()) != 0;
}
protected String flag2Set = null;
public String getFlag2Set(){return flag2Set;}
public void setFlag2Set(String in){flag2Set=in;}
protected int flag2Numeric = -1;
public int getFlag2Numeric(){return flag2Numeric;}
public void setFlag2Numeric(int i){flag2Numeric=i;}
protected String flag3Set = null;
public String getFlag3Set(){return flag3Set;}
public void setFlag3Set(String in){flag3Set=in;}
protected int flag3Numeric = -1;
public int getFlag3Numeric(){return flag3Numeric;}
public void setFlag3Numeric(int i){flag3Numeric=i;}
public List<String> getKeywords() { return keywords; }
public void setKeywords(List<String> keywords) {this.keywords = keywords;}
public String getKeywordString(){
String rv = "";
List keywords=getKeywords();
if (getKeywords()!=null){
Iterator<String> it1 = getKeywords().iterator();
TreeSet<String> keywordSet = new TreeSet<String>(new Comparator<String>() {
public int compare( String first, String second ) {
if (first==null) {
return 1;
}
if (second==null) {
return -1;
}
Collator collator = Collator.getInstance();
return collator.compare(first,second);
}
});
while( it1.hasNext() ){
keywordSet.add(it1.next());
}
Iterator<String> it2 = keywordSet.iterator();
while (it2.hasNext()) {
rv+= it2.next();
if( it2.hasNext())
rv+=", ";
}
}
return rv;
}
public List<Keyword> getKeywordObjects() { return keywordObjects; }
public void setKeywordObjects(List<Keyword> keywords) {this.keywordObjects = keywords;}
public Float getSearchBoost() { return searchBoost; }
public void setSearchBoost(Float boost) { searchBoost = boost; }
/**
* Sorts the ents2ents records into the proper order for display.
*
*/
public void sortForDisplay(){
if( sorted ) return;
if( getObjectPropertyList() == null ) return;
sortPropertiesForDisplay();
sortEnts2EntsForDisplay();
sorted = true;
}
protected void sortEnts2EntsForDisplay(){
if( getObjectPropertyList() == null ) return;
Iterator it = getObjectPropertyList().iterator();
while(it.hasNext()){
ObjectProperty prop = (ObjectProperty)it.next();
prop.sortObjectPropertyStatementsForDisplay(prop,prop.getObjectPropertyStatements());
}
}
protected void sortPropertiesForDisplay( ){
//here we sort the Property objects
Collections.sort(getObjectPropertyList(), new ObjectProperty.DisplayComparator());
}
public static final String [] INCLUDED_IN_JSON = {
"URI",
"name",
"moniker",
"vClassId"
};
public JSONObject toJSON() throws JSONException {
JSONObject jsonObj = new JSONObject(this, INCLUDED_IN_JSON);
return jsonObj;
}
/**
*
* @param fieldName- expected to be the field name in the format
* @return
* @throws NoSuchMethodException
*/
public Object getField(String fieldName) throws NoSuchMethodException{
if( fieldName == null || fieldName.length() == 0) return null;
if( "name".equalsIgnoreCase(fieldName) )
return getName();
if( "timekey".equalsIgnoreCase(fieldName) )
return getTimekey();
//not one of the more common ones, try reflection
//capitalize first letter
String methodName = "get" + fieldName.substring(0,1).toUpperCase()
+ fieldName.substring(1,fieldName.length());
Class cls = this.getClass();
try {
Method meth = cls.getMethod(methodName, (Class[]) null);
return meth.invoke(this,(Object[])null);
} catch (Exception e) { }
//should never get here
throw new NoSuchMethodException("Entity.getField() attempt to use a method called "
+ methodName +"() for field " + fieldName + " but the method doesn't exist.");
}
public int compareTo(Individual o2) {
Collator collator = Collator.getInstance();
if (o2 == null) {
return 1;
} else {
return collator.compare(this.getName(),o2.getName());
}
}
}

View file

@ -25,7 +25,6 @@ import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.ModelMaker;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
@ -42,6 +41,10 @@ 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.search.beans.VitroQuery;
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryWrapper;
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
@ -97,6 +100,13 @@ public class EntityController extends VitroHttpServlet {
doNotFound(vreq, res);
return;
}
// If this is an uploaded file, redirect to its "alias URL".
String aliasUrl = getAliasUrlForBytestreamIndividual(indiv);
if (aliasUrl != null) {
res.sendRedirect(req.getContextPath() + aliasUrl);
return;
}
doHtml( vreq, res , indiv);
return;
@ -476,6 +486,47 @@ public class EntityController extends VitroHttpServlet {
// TODO Auto-generated method stub
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)
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.");
return null;
}
log.debug("Alias URL for '" + entity.getURI() + "' is '" + url + "'");
return url;
}
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth ) {
Resource subj = newModel.getResource(entity.getURI());
@ -578,7 +629,7 @@ public class EntityController extends VitroHttpServlet {
out.println("<p>id is the id of the entity to query for. netid also works.</p>");
out.println("</body></html>");
}
private void doNotFound(HttpServletRequest req, HttpServletResponse res)
throws IOException, ServletException {
VitroRequest vreq = new VitroRequest(req);

View file

@ -116,8 +116,6 @@ public class CloneEntityServlet extends BaseEditController {
ind.setBlurb("");
ind.setDescription("");
ind.setCitation("");
ind.setImageFile("");
ind.setImageThumb("");
String cloneURI=individualDao.insertNewIndividual(ind);
if (cloneURI == null){ log.error("Error inserting cloned individual"); return; }

View file

@ -43,6 +43,10 @@ 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.search.beans.VitroQuery;
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryWrapper;
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
@ -109,6 +113,13 @@ public class IndividualController extends FreeMarkerHttpServlet {
return;
}
// If this is an uploaded file, redirect to its "alias URL".
String aliasUrl = getAliasUrlForBytestreamIndividual(indiv);
if (aliasUrl != null) {
res.sendRedirect(req.getContextPath() + aliasUrl);
return;
}
doHtml( vreq, res , indiv);
return;
@ -488,6 +499,47 @@ 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)
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.");
return null;
}
log.debug("Alias URL for '" + entity.getURI() + "' is '" + url + "'");
return url;
}
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth ) {
Resource subj = newModel.getResource(entity.getURI());

View file

@ -2,18 +2,14 @@
package edu.cornell.mannlib.vitro.webapp.dao;
import java.util.Arrays;
import java.util.List;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.vocabulary.XSD;
import com.hp.hpl.jena.ontology.AnnotationProperty;
public class VitroVocabulary {
public static final String vitroURI = "http://vitro.mannlib.cornell.edu/ns/vitro/0.7#";
public static final String VITRO_PUBLIC = "http://vitro.mannlib.cornell.edu/ns/vitro/public#";
/** BJL23 2008-02-25:
* This is a hack. The classic Vitro code is heavily reliant on simple identifiers, and it will take some doing to completely
@ -91,8 +87,6 @@ public class VitroVocabulary {
public static final String DISPLAY_LIMIT = vitroURI+"displayLimitAnnot";
public static final String CITATION = vitroURI+"citation";
public static final String IMAGEFILE = vitroURI+"imageFile";
public static final String IMAGETHUMB = vitroURI+"imageThumb";
// ================== property related =================================
@ -284,5 +278,18 @@ public class VitroVocabulary {
public static final String ONTOLOGY_PREFIX_ANNOT = vitroURI + "ontologyPrefixAnnot";
// =============== file storage vocabulary ================================
public static final String FS_FILE_CLASS = VITRO_PUBLIC + "File";
public static final String FS_BYTESTREAM_CLASS = VITRO_PUBLIC + "FileByteStream";
public static final String FS_FILENAME = VITRO_PUBLIC + "filename";
public static final String FS_MIME_TYPE = VITRO_PUBLIC + "mimeType";
public static final String FS_ATTRIBUTION = VITRO_PUBLIC + "attribution";
public static final String FS_DOWNLOAD_LOCATION = VITRO_PUBLIC + "downloadLocation";
public static final String FS_THUMBNAIL_IMAGE = VITRO_PUBLIC + "thumbnailImage";
public static final String IND_MAIN_IMAGE = VITRO_PUBLIC + "mainImage";
public static final String IND_IMAGE = VITRO_PUBLIC + "image";
}

View file

@ -242,18 +242,24 @@ public class IndividualFiltering implements Individual {
return _innerIndividual.getFlag3Set();
}
@Override
public String getMainImageUri() {
return _innerIndividual.getMainImageUri();
}
public String getImageFile() {
return _innerIndividual.getImageFile();
}
@Override
public String getImageUrl() {
return _innerIndividual.getImageUrl();
}
public String getImageThumb() {
return _innerIndividual.getImageThumb();
}
@Override
public String getThumbUrl() {
return _innerIndividual.getThumbUrl();
}
public List<String> getKeywords() {
public List<String> getKeywords() {
return _innerIndividual.getKeywords();
}
@ -405,15 +411,10 @@ public class IndividualFiltering implements Individual {
}
public void setImageFile(String imageFile) {
_innerIndividual.setImageFile(imageFile);
}
public void setImageThumb(String imageThumb) {
_innerIndividual.setImageThumb(imageThumb);
}
@Override
public void setMainImageUri(String mainImageUri) {
_innerIndividual.setMainImageUri(mainImageUri);
}
public void setKeywords(List<String> keywords) {
_innerIndividual.setKeywords(keywords);

View file

@ -21,7 +21,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.DateTime;
import com.hp.hpl.jena.ontology.DatatypeProperty;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntResource;
@ -56,7 +55,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.InsertException;
import edu.cornell.mannlib.vitro.webapp.dao.KeywordDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualCreationEvent;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualDeletionEvent;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualUpdateEvent;
@ -313,8 +311,10 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao {
addPropertyDateTimeValue(ind,SUNSET,ent.getSunset(), ontModel);
addPropertyDateTimeValue(ind,TIMEKEY,ent.getTimekey(), ontModel);
addPropertyDateTimeValue(ind,MODTIME,Calendar.getInstance().getTime(),ontModel);
addPropertyStringValue(ind,IMAGETHUMB,ent.getImageThumb(),ontModel);
addPropertyStringValue(ind,IMAGEFILE,ent.getImageFile(),ontModel);
if (ent.getMainImageUri() != null) {
addPropertyResourceURIValue(ind, IND_MAIN_IMAGE, ent.getMainImageUri());
}
if (ent.getAnchor()!= null && ent.getAnchor().length()>0 && LINK != null) {
com.hp.hpl.jena.ontology.Individual primaryLink = ontModel.createIndividual(entURI+"_primaryLink", LINK);
primaryLink.addProperty(RDF.type, LINK);
@ -379,8 +379,9 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao {
ent.getFlag1Numeric();
ent.getFlag1Set();
ent.getFlag2Set();
ent.getImageFile();
ent.getImageThumb();
ent.getMainImageUri();
ent.getImageUrl();
ent.getThumbUrl();
ent.getKeywords();
ent.getKeywordString();
ent.getLinksList();
@ -472,9 +473,8 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao {
updatePropertyDateTimeValue(ind,SUNRISE,ent.getSunrise(), ontModel);
updatePropertyDateTimeValue(ind,SUNSET,ent.getSunset(), ontModel);
updatePropertyDateTimeValue(ind,TIMEKEY,ent.getTimekey(), ontModel);
updatePropertyStringValue(ind,IMAGETHUMB,ent.getImageThumb(),ontModel);
updatePropertyStringValue(ind,IMAGEFILE,ent.getImageFile(),ontModel);
updatePropertyDateTimeValue(ind,MODTIME,Calendar.getInstance().getTime(),ontModel);
updatePropertyResourceURIValue(ind, IND_MAIN_IMAGE, ent.getMainImageUri(), ontModel);
if (ent.getAnchor()!= null && ent.getAnchor().length()>0) {
if (LINK != null && PRIMARY_LINK != null) {
boolean updatedExisting = false;

View file

@ -40,6 +40,8 @@ import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
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.utils.FlagMathUtils;
public class IndividualJena extends IndividualImpl implements Individual {
@ -474,33 +476,54 @@ public class IndividualJena extends IndividualImpl implements Individual {
}
}
public String getImageFile() {
if (this.imageFile != null) {
return imageFile;
} else {
ind.getOntModel().enterCriticalSection(Lock.READ);
try {
imageFile = webappDaoFactory.getJenaBaseDao().getPropertyStringValue(ind,webappDaoFactory.getJenaBaseDao().IMAGEFILE);
return imageFile;
} finally {
ind.getOntModel().leaveCriticalSection();
}
}
}
@Override
public String getMainImageUri() {
if (this.mainImageUri != NOT_INITIALIZED) {
return mainImageUri;
} else {
for (ObjectPropertyStatement stmt : getObjectPropertyStatements()) {
if (stmt.getPropertyURI()
.equals(VitroVocabulary.IND_MAIN_IMAGE)) {
mainImageUri = stmt.getObjectURI();
return mainImageUri;
}
}
return null;
}
}
public String getImageThumb() {
if (this.imageThumb != null) {
return imageThumb;
} else {
ind.getOntModel().enterCriticalSection(Lock.READ);
try {
imageThumb = webappDaoFactory.getJenaBaseDao().getPropertyStringValue(ind,webappDaoFactory.getJenaBaseDao().IMAGETHUMB);
return imageThumb;
} finally {
ind.getOntModel().leaveCriticalSection();
}
}
}
@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;
}
}
@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;
}
}
public String getAnchor() {
if (this.anchor != null) {

View file

@ -60,8 +60,6 @@ public class JenaBaseDaoCon {
protected DatatypeProperty MODTIME = _constModel.createDatatypeProperty(VitroVocabulary.MODTIME);
protected DatatypeProperty TIMEKEY = _constModel.createDatatypeProperty(VitroVocabulary.TIMEKEY);
protected DatatypeProperty CITATION = _constModel.createDatatypeProperty(VitroVocabulary.CITATION);
protected DatatypeProperty IMAGETHUMB = _constModel.createDatatypeProperty(VitroVocabulary.IMAGETHUMB);
protected DatatypeProperty IMAGEFILE = _constModel.createDatatypeProperty(VitroVocabulary.IMAGEFILE);
protected DatatypeProperty DISPLAY_RANK = _constModel.createDatatypeProperty(VitroVocabulary.DISPLAY_RANK);
protected AnnotationProperty DISPLAY_RANK_ANNOT = _constModel.createAnnotationProperty(VitroVocabulary.DISPLAY_RANK_ANNOT);
@ -183,6 +181,17 @@ public class JenaBaseDaoCon {
protected AnnotationProperty ONTOLOGY_PREFIX_ANNOT = _constModel.createAnnotationProperty(VitroVocabulary.ONTOLOGY_PREFIX_ANNOT);
protected OntClass FS_FILE = _constModel.createClass(VitroVocabulary.FS_FILE_CLASS);
protected OntClass FS_BYTESTREAM = _constModel.createClass(VitroVocabulary.FS_BYTESTREAM_CLASS);
protected ObjectProperty FS_DOWNLOAD_LOCATION = _constModel.createObjectProperty(VitroVocabulary.FS_DOWNLOAD_LOCATION);
protected ObjectProperty FS_THUMBNAIL_IMAGE = _constModel.createObjectProperty(VitroVocabulary.FS_THUMBNAIL_IMAGE);
protected DatatypeProperty FS_FILENAME = _constModel.createDatatypeProperty(VitroVocabulary.FS_FILENAME);
protected DatatypeProperty FS_MIME_TYPE = _constModel.createDatatypeProperty(VitroVocabulary.FS_MIME_TYPE);
protected DatatypeProperty FS_ATTRIBUTION = _constModel.createDatatypeProperty(VitroVocabulary.FS_ATTRIBUTION);
protected ObjectProperty IND_MAIN_IMAGE = _constModel.createObjectProperty(VitroVocabulary.IND_MAIN_IMAGE);
protected ObjectProperty IND_IMAGE = _constModel.createObjectProperty(VitroVocabulary.IND_IMAGE);
public OntModel getConstModel() {
return _constModel;
}

View file

@ -36,7 +36,7 @@ public class TabEntityFactoryGalleryJena extends TabEntityFactoryJena
implements TabEntityFactory {
private TabEntityFactory _innerFactory = null;
public final UnaryFunctor<Individual,Boolean>onlyWithThumbs = new OnlyWithThumbs();
public final UnaryFunctor<Individual,Boolean>onlyWithMainImage = new OnlyWithMainImage();
public TabEntityFactoryGalleryJena(TabEntityFactory innerEntFactory, Tab tab, int auth_level, ApplicationBean appBean, WebappDaoFactoryJena wadf) {
super(tab, auth_level, appBean, wadf);
@ -79,7 +79,7 @@ public class TabEntityFactoryGalleryJena extends TabEntityFactoryJena
return Collections.EMPTY_LIST;
List filteredEnts = new LinkedList( );
Filter.filter(ents,onlyWithThumbs,filteredEnts);
Filter.filter(ents,onlyWithMainImage,filteredEnts);
if( filteredEnts.size() <= numberOfrequestedEnts)
return filteredEnts;
@ -94,10 +94,10 @@ public class TabEntityFactoryGalleryJena extends TabEntityFactoryJena
return entsOut;
}
private class OnlyWithThumbs extends UnaryFunctor<Individual,Boolean>{
private class OnlyWithMainImage extends UnaryFunctor<Individual,Boolean>{
@Override
public Boolean fn(Individual arg) {
return arg.getImageThumb() != null;
return arg.getMainImageUri()!= null;
}
}
}

View file

@ -0,0 +1,411 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage;
import java.util.List;
import org.apache.log4j.Logger;
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;
/**
* <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>
*/
public class FileModelHelper {
private static final Logger log = Logger.getLogger(FileModelHelper.class);
// ----------------------------------------------------------------------
// 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.");
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;
}
/**
* Store this file surrogate as the thumnail 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) {
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);
individualDao.deleteIndividual(bytestream);
individualDao.deleteIndividual(surrogate);
}
}

View file

@ -0,0 +1,109 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage;
import org.apache.log4j.Logger;
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
/**
* Static methods to help when serving uploaded files.
*/
public class FileServingHelper {
private static final Logger log = Logger.getLogger(FileServingHelper.class);
private static final String DEFAULT_PATH = "/individual/";
private static final String FILE_PATH = "/file/";
private static final String DEFAULT_NAMESPACE = initializeDefaultNamespace();
/**
* At startup, get the default namespace from the configuration properties,
* and trim off the suffix.
*/
private static String initializeDefaultNamespace() {
String defaultNamespace = ConfigurationProperties
.getProperty(FileStorageSetup.PROPERTY_DEFAULT_NAMESPACE);
if (defaultNamespace == null) {
throw new IllegalArgumentException(
"Configuration properties must contain a value for '"
+ FileStorageSetup.PROPERTY_DEFAULT_NAMESPACE + "'");
}
if (!defaultNamespace.endsWith(DEFAULT_PATH)) {
throw new IllegalArgumentException(
"Default namespace does not match the expected form: '"
+ defaultNamespace + "'");
}
return defaultNamespace;
}
/**
* <p>
* Combine the URI and the filename to produce a relative URL for the file
* (relative to the context of the webapp).
* </p>
* <p>
* This should involve stripping the default namespace from the front of the
* URL, replacing it with the file prefix, and adding the filename to the
* end.
* </p>
*
* @return <ul>
* <li>the translated URL, if the URI was in the default namespace,</li>
* <li>the original URI, if it wasn't in the default namespace,</li>
* <li>null, if the original URI or the filename was null.</li>
* </ul>
*/
public static String getBytestreamAliasUrl(String uri, String filename) {
if ((uri == null) || (filename == null)) {
return null;
}
if (!uri.startsWith(DEFAULT_NAMESPACE)) {
log.warn("uri does not start with the default namespace: '" + uri
+ "'");
return uri;
}
String remainder = uri.substring(DEFAULT_NAMESPACE.length());
String separator = remainder.endsWith("/") ? "" : "/";
return FILE_PATH + remainder + separator + filename;
}
/** No need for instances because all of the methods are static. */
private FileServingHelper() {
// nothing to instantiate.
}
/**
* <p>
* Take a relative URL (relative to the context of the webapp) and produce
* the URI for the file bytestream.
* </p>
* <p>
* This should involve removing the filename from the end of the URL, and
* replacing the file prefix with the default namespace.
* </p>
*
* @return the URI, or <code>null</code> if the URL couldn't be translated.
*/
public static String getBytestreamUri(String path) {
if (path == null) {
return null;
}
if (!path.startsWith(FILE_PATH)) {
log.warn("path does not start with a file prefix: '" + path + "'");
return null;
}
String remainder = path.substring(FILE_PATH.length());
int slashHere = remainder.lastIndexOf('/');
if (slashHere == -1) {
log.debug("path does not include a filename: '" + path + "'");
return null;
}
remainder = remainder.substring(0, slashHere);
return DEFAULT_NAMESPACE + remainder;
}
}

View file

@ -23,10 +23,13 @@ import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
/**
* The default implementation of {@link FileStorage}.
*/
public class FileStorageImpl implements FileStorage {
private static final Logger log = Logger.getLogger(FileStorageImpl.class);
private final File baseDir;
private final File rootDir;
@ -290,6 +293,8 @@ public class FileStorageImpl implements FileStorage {
public String getFilename(String id) throws IOException {
File dir = FileStorageHelper.getPathToIdDirectory(id,
this.namespacesMap, this.rootDir);
log.debug("ID '" + id + "' translates to this directory path: '" + dir
+ "'");
if ((!dir.exists()) || (!dir.isDirectory())) {
return null;

View file

@ -28,13 +28,13 @@ public class FileStorageSetup implements ServletContextListener {
* The default implementation will use this key to ask
* {@link ConfigurationProperties} for the file storage base directory.
*/
static final String PROPERTY_FILE_STORAGE_BASE_DIR = "upload.directory";
public static final String PROPERTY_FILE_STORAGE_BASE_DIR = "upload.directory";
/**
* The default implementation will use this key to ask
* {@link ConfigurationProperties} for the default URI namespace.
*/
static final String PROPERTY_DEFAULT_NAMESPACE = "Vitro.defaultNamespace";
public static final String PROPERTY_DEFAULT_NAMESPACE = "Vitro.defaultNamespace";
/**
* Create an implementation of {@link FileStorage} and store it in the
@ -46,7 +46,7 @@ public class FileStorageSetup implements ServletContextListener {
FileStorage fs;
try {
File baseDirectory = figureBaseDir();
Collection<String> fileNamespace = figureFileNamespace();
Collection<String> fileNamespace = confirmDefaultNamespace();
fs = new FileStorageImpl(baseDirectory, fileNamespace);
} catch (IOException e) {
throw new IllegalStateException(
@ -75,16 +75,15 @@ public class FileStorageSetup implements ServletContextListener {
}
/**
* Get the configuration property for the default namespace, and derive the
* file namespace from it. The default namespace is assumed to be in this
* form: <code>http://vivo.mydomain.edu/individual/</code>
* Get the configuration property for the default namespace, and confirm
* that it is in the proper form. The default namespace is assumed to be in
* this form: <code>http://vivo.mydomain.edu/individual/</code>
*
* For use by the constructor in implementations of {@link FileStorage}.
*
* @returns the file namespace is assumed to be in this form:
* <code>http://vivo.mydomain.edu/file/</code>
* @returns a collection containing the default namespace.
*/
private Collection<String> figureFileNamespace() {
private Collection<String> confirmDefaultNamespace() {
String defaultNamespace = ConfigurationProperties
.getProperty(PROPERTY_DEFAULT_NAMESPACE);
if (defaultNamespace == null) {
@ -94,7 +93,6 @@ public class FileStorageSetup implements ServletContextListener {
}
String defaultSuffix = "/individual/";
String fileSuffix = "/file/";
if (!defaultNamespace.endsWith(defaultSuffix)) {
throw new IllegalArgumentException(
@ -102,10 +100,7 @@ public class FileStorageSetup implements ServletContextListener {
+ defaultNamespace + "'");
}
int hostLength = defaultNamespace.length() - defaultSuffix.length();
String fileNamespace = defaultNamespace.substring(0, hostLength)
+ fileSuffix;
return Collections.singleton(fileNamespace);
return Collections.singleton(defaultNamespace);
}
@Override

View file

@ -0,0 +1,175 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.serving;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
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;
/**
* <p>
* Handles a request to serve an uploaded file from the file storage system.
* </p>
* <p>
* The path of the request should be the "alias URL" of the desired file. We
* need to:
* <ul>
* <li>Use the alias URL to find the URI of the file bytestream object.</li>
* <li>Find the file surrogate object to get the MIME type of the file, and
* confirm the filename.</li>
* <li>Set the MIME type on the output stream and serve the bytes.</li>
* </ul>
* </p>
* <p>
* If the request is superficially correct, but no such file can be found,
* return a 404. If there is a break in the data structures within the model or
* the file system, return a 500.
* </p>
*/
public class FileServingServlet extends VitroHttpServlet {
private static final Logger log = Logger
.getLogger(FileServingServlet.class);
private FileStorage fileStorage;
/**
* Get a reference to the File Storage system.
*/
@Override
public void init() throws ServletException {
Object o = getServletContext().getAttribute(
FileStorageSetup.ATTRIBUTE_NAME);
if (o instanceof FileStorage) {
fileStorage = (FileStorage) o;
} else {
throw new UnavailableException(
"The ServletContext did not hold a FileStorage object at '"
+ FileStorageSetup.ATTRIBUTE_NAME
+ "'; found this instead: " + o);
}
}
@Override
protected void doGet(HttpServletRequest rawRequest,
HttpServletResponse response) throws ServletException, IOException {
VitroRequest request = new VitroRequest(rawRequest);
// Use the alias URL to get the URI of the bytestream object.
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) {
String message = "The request path is not valid for the File servlet: '"
+ path + "'";
log.error(message);
response.sendError(SC_INTERNAL_SERVER_ERROR, message);
return;
}
// 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;
}
if (!actualFilename.equals(requestedFilename)) {
log.warn("The requested filename does not match the "
+ "actual filename; request: '" + path + "', actual: '"
+ actualFilename + "'");
response.sendError(SC_NOT_FOUND, ("File not found: " + path));
return;
}
// Get the MIME type.
String mimeType = new FileModelHelper(getWebappDaoFactory())
.getMimeTypeForBytestream(uri);
// Open the actual byte stream.
InputStream in;
try {
in = fileStorage.getInputStream(uri, actualFilename);
} catch (FileNotFoundException e) {
log.error(e);
response.sendError(SC_INTERNAL_SERVER_ERROR, e.toString());
return;
}
/*
* Everything is ready and working. Set the status and the content type,
* and send the image bytes.
*/
response.setStatus(SC_OK);
if (mimeType != null) {
response.setContentType(mimeType);
}
ServletOutputStream out = null;
try {
out = response.getOutputStream();
byte[] buffer = new byte[8192];
int howMany;
while (-1 != (howMany = in.read(buffer))) {
out.write(buffer, 0, howMany);
}
} finally {
try {
in.close();
} catch (Exception e) {
e.printStackTrace();
}
if (out != null) {
try {
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* The filename is the portion of the path after the last slash.
*/
private String getFilename(String path) {
int slashHere = path.lastIndexOf('/');
if (slashHere == -1) {
return path;
} else {
return path.substring(slashHere + 1);
}
}
/**
* A POST request is treated the same as a GET request.
*/
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

View file

@ -0,0 +1,71 @@
/* $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.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
/**
* Adjust any individual that has a thumbnail with no main image.
*/
public class AllThumbsAdjuster extends FsuScanner {
protected final File imageDirectory;
public AllThumbsAdjuster(FSUController controller) {
super(controller);
this.imageDirectory = controller.getImageDirectory();
}
/**
* 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.");
ResIterator haveThumb = model.listResourcesWithProperty(thumbProperty);
try {
while (haveThumb.hasNext()) {
Resource resource = haveThumb.next();
if (resource.getProperty(imageProperty) == null) {
createMainImageFromThumbnail(resource);
}
}
} finally {
haveThumb.close();
}
}
/**
* 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 = new File(imageDirectory, thumbFilename);
File mainFile = new File(imageDirectory, mainFilename);
mainFile = checkNameConflicts(mainFile);
FileUtil.copyFile(thumbFile, mainFile);
resource.addProperty(imageProperty, mainFilename);
} catch (IOException e) {
updateLog.error(resource, "failed to create main file '"
+ mainFilename + "'", e);
}
}
}

View file

@ -0,0 +1,73 @@
/* $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.ResIterator;
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 {
protected final File imageDirectory;
public DeadEndPropertyRemover(FSUController controller) {
super(controller);
this.imageDirectory = controller.getImageDirectory();
}
/**
* 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) {
ResIterator resources = model.listResourcesWithProperty(prop);
try {
while (resources.hasNext()) {
Resource resource = resources.next();
removeDeadEndPropertiesFromResource(resource, prop, label);
}
} finally {
resources.close();
}
}
/**
* 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 = new File(imageDirectory, filename);
if (!file.exists()) {
updateLog.warn(resource, "removing link to " + label + " '"
+ filename + "': file does not exist at '"
+ file.getAbsolutePath() + "'.");
model.remove(stmt);
}
}
}
}
}

View file

@ -0,0 +1,38 @@
/* $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.FileModelHelper;
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 directory where the old-style images were stored. */
File getImageDirectory();
/** 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();
/** Where to store the files that weren't in use anyway. */
File getUnreferencedDirectory();
}

View file

@ -0,0 +1,180 @@
/* $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) throws IOException {
this.logFile = generateTimestampedFilename(logDirectory);
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) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-sss");
String filename = "upgradeLog." + sdf.format(new Date()) + ".txt";
return new File(upgradeDirectory, filename);
}
/**
* Where are we writing the output?
*/
String getFilename() {
return this.logFile.getAbsolutePath();
}
/**
* Write this message.
*/
void log(String message) {
writer.println(timeStamper.format(new Date()) + " INFO " + message);
}
/**
* Write this message about this resource.
*/
void log(Resource resource, String message) {
log(showResource(resource) + message);
}
/**
* Write this warning message.
*/
public void warn(String message) {
writer.println(timeStamper.format(new Date()) + " WARN " + message);
}
/**
* Write this warning message about this resource.
*/
public void warn(Resource resource, String message) {
warn(showResource(resource) + message);
}
/**
* Write this error message.
*/
void error(String message) {
writer.println(timeStamper.format(new Date()) + " ERROR " + message);
}
/**
* Write this exception as an error message..
*/
void error(Exception e) {
error(e.toString());
e.printStackTrace(writer);
}
/**
* Write an error message with this exception.
*/
public void error(String message, Exception e) {
error(message);
e.printStackTrace(writer);
}
/**
* Write an error message about this resource and with this exception.
*/
public void error(Resource resource, String message) {
error(showResource(resource) + message);
}
/**
* Write an error message about this resource and with this exception.
*/
public void error(Resource resource, String message, Exception e) {
error(showResource(resource) + message, e);
}
/**
* Write a section heading.
*/
public void section(String message) {
log(">>>>>>>>>> ");
log(">>>>>>>>>> " + message);
log(">>>>>>>>>> ");
}
/**
* Close the writer, if not already closed.
*/
public void close() {
if (open) {
writer.close();
open = false;
}
}
/**
* Format the resource label and URI for output in a message.
*/
private String showResource(Resource resource) {
return "On resource '" + getLabel(resource) + "' (" + getUri(resource)
+ "), ";
}
/**
* Find the URI for this resource, if there is one.
*/
private String getUri(Resource resource) {
if (resource != null) {
String uri = resource.getURI();
if (uri != null) {
return uri;
}
}
return "no URI";
}
/**
* Find the label for this resource, if there is one.
*/
private String getLabel(Resource resource) {
if (resource != null) {
Model model = resource.getModel();
if (model != null) {
Property prop = model.createProperty(VitroVocabulary.LABEL);
Statement stmt = resource.getProperty(prop);
if (stmt != null) {
RDFNode node = stmt.getObject();
if (node.isLiteral()) {
return ((Literal) node).getString();
}
}
}
}
return "no label";
}
}

View file

@ -0,0 +1,272 @@
/* $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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ResIterator;
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.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 = 150;
/** How high should a generated thumbnail image be (in pixels)? */
public static final int THUMBNAIL_HEIGHT = 150;
/** 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 FileModelHelper fileModelHelper;
private final File imageDirectory;
private final File upgradeDirectory;
private FSULog updateLog;
public FileStorageUpdater(WebappDaoFactory wadf, Model model,
FileStorage fileStorage, File uploadDirectory) {
this.model = model;
this.fileStorage = fileStorage;
this.fileModelHelper = new FileModelHelper(wadf);
this.imageDirectory = new File(uploadDirectory, "images");
this.upgradeDirectory = new File(uploadDirectory, "upgrade");
}
/**
* <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);
} 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() {
ResIterator haveImage = model.listResourcesWithProperty(model
.createProperty(IMAGEFILE));
try {
if (haveImage.hasNext()) {
return true;
}
} finally {
haveImage.close();
}
ResIterator haveThumb = model.listResourcesWithProperty(model
.createProperty(IMAGETHUMB));
try {
if (haveThumb.hasNext()) {
return true;
}
} finally {
haveThumb.close();
}
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);
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 FileModelHelper getFileModelHelper() {
return this.fileModelHelper;
}
@Override
public FileStorage getFileStorage() {
return this.fileStorage;
}
@Override
public File getImageDirectory() {
return this.imageDirectory;
}
@Override
public File getTranslatedDirectory() {
return new File(this.upgradeDirectory, "translatedImages");
}
@Override
public File getUnreferencedDirectory() {
return new File(this.upgradeDirectory, "unreferencedImages");
}
}

View file

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

View file

@ -0,0 +1,122 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.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;
/**
* 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>();
StmtIterator stmts = resource.listProperties(property);
try {
while (stmts.hasNext()) {
list.add(stmts.next());
}
} finally {
stmts.close();
}
return list;
}
/**
* Find the filename within a path so we can add this prefix to it, while
* retaining the path.
*/
protected String addFilenamePrefix(String prefix, String path) {
int slashHere = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
if (slashHere == -1) {
return prefix + path;
} else {
String dirs = path.substring(0, slashHere + 1);
String filename = path.substring(slashHere + 1);
return dirs + prefix + filename;
}
}
/**
* We are about to create a file - if a file of this name already exists,
* increment the name until we have no collision.
*
* @return the original file, or the file with the incremented name.
*/
protected File checkNameConflicts(final File file) {
if (!file.exists()) {
// No conflict.
return file;
}
File parent = file.getParentFile();
String filename = file.getName();
for (int i = 0; i < 100; i++) {
File newFile = new File(parent, i + filename);
if (!newFile.exists()) {
updateLog.log("File '" + file + "' already exists, using '"
+ newFile + "' to avoid conflict.");
return newFile;
}
}
updateLog.error("File '" + file
+ "' already exists. Unable to avoid conflict.");
return file;
}
}

View file

@ -0,0 +1,121 @@
/* $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 {
protected final File imageDirectory;
protected final File translatedDirectory;
protected final File unreferencedDirectory;
public ImageDirectoryCleaner(FSUController controller) {
super(controller);
this.imageDirectory = controller.getImageDirectory();
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(imageDirectory);
}
/**
* Move all of the files that we translated into the new system.
*/
private void removeTranslatedFiles(Collection<String> translatedFiles) {
for (String path : translatedFiles) {
updateLog.log("moving image file '" + path
+ "' to the 'translated' directory.");
File oldFile = new File(imageDirectory, path);
File deletedFile = new File(translatedDirectory, path);
try {
FileUtil.moveFile(oldFile, deletedFile);
} catch (IOException e) {
updateLog.error("Failed to move translated file '"
+ oldFile.getAbsolutePath() + "'");
}
}
}
/**
* 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 = imageDirectory.getAbsolutePath();
String thisPath = directory.getAbsolutePath();
if (!thisPath.startsWith(imagesPath)) {
throw new IOException("Can't make a corresponding directory for '"
+ thisPath + "'");
}
String suffix = thisPath.substring(imagesPath.length());
File corresponding = new File(unreferencedDirectory, suffix);
corresponding.mkdirs();
if (!corresponding.exists()) {
throw new IOException("Failed to create corresponding directory '"
+ corresponding.getAbsolutePath() + "'");
}
return corresponding;
}
}

View file

@ -0,0 +1,191 @@
/* $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.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
/**
* 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 {
protected final File imageDirectory;
protected final FileModelHelper fileModelHelper;
protected final FileStorage fileStorage;
public ImageSchemaTranslater(FSUController controller) {
super(controller);
this.imageDirectory = controller.getImageDirectory();
this.fileStorage = controller.getFileStorage();
this.fileModelHelper = controller.getFileModelHelper();
}
/**
* 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;
}
translateMainImage(resource, mainImages.get(0));
translated.add(mainImages.get(0));
resource.removeAll(imageProperty);
List<String> thumbnails = getValues(resource, thumbProperty);
if (thumbnails.size() != 1) {
updateLog.error(resource, "has " + thumbnails.size()
+ " thumbnails: " + thumbnails);
return;
}
translateThumbnail(resource, thumbnails.get(0));
translated.add(thumbnails.get(0));
resource.removeAll(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>
* </ul>
*
* @return the new File surrogate.
*/
private Individual translateFile(Resource resource, String path,
String label) {
File oldFile = new File(imageDirectory, 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);
} catch (IOException e) {
updateLog.error(resource, "Can't create the " + label + " file. ",
e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}
/**
* Remove any path parts, and just get the filename and extension.
*/
private String getSimpleFilename(String path) {
return FilenameUtils.getName(path);
}
/**
* Guess what the MIME type might be.
*/
private String guessMimeType(Resource resource, String filename) {
if (filename.endsWith(".gif") || filename.endsWith(".GIF")) {
return "image/gif";
} else if (filename.endsWith(".png") || filename.endsWith(".PNG")) {
return "image/png";
} else if (filename.endsWith(".jpg") || filename.endsWith(".JPG")) {
return "image/jpeg";
} else if (filename.endsWith(".jpeg") || filename.endsWith(".JPEG")) {
return "image/jpeg";
} else if (filename.endsWith(".jpe") || filename.endsWith(".JPE")) {
return "image/jpeg";
} else {
updateLog.warn(resource,
"can't recognize the MIME type of this image file: '"
+ filename + "'");
return "image";
}
}
}

View file

@ -0,0 +1,71 @@
/* $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.ResIterator;
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) {
ResIterator resources = model.listResourcesWithProperty(prop);
try {
while (resources.hasNext()) {
Resource resource = resources.next();
removeExtraProperties(resource, prop, label);
}
} finally {
resources.close();
}
}
/**
* 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 + "'");
}
model.remove(stmt);
}
}
}

View file

@ -0,0 +1,103 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
/**
* Adjust any individual that has a main image but no thumbnail.
*/
public class NoThumbsAdjuster extends FsuScanner {
protected final File imageDirectory;
public NoThumbsAdjuster(FSUController controller) {
super(controller);
this.imageDirectory = controller.getImageDirectory();
}
/**
* 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.");
ResIterator haveImage = model.listResourcesWithProperty(imageProperty);
try {
while (haveImage.hasNext()) {
Resource resource = haveImage.next();
if (resource.getProperty(thumbProperty) == null) {
createThumbnailFromMainImage(resource);
}
}
} finally {
haveImage.close();
}
}
/**
* 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 = new File(imageDirectory, mainFilename);
File thumbFile = new File(imageDirectory, thumbFilename);
thumbFile = checkNameConflicts(thumbFile);
try {
generateThumbnailImage(mainFile, thumbFile,
FileStorageUpdater.THUMBNAIL_WIDTH,
FileStorageUpdater.THUMBNAIL_HEIGHT);
resource.addProperty(thumbProperty, thumbFilename);
} catch (IOException e) {
updateLog.error(resource, "failed to create thumbnail file '"
+ thumbFilename + "'", e);
}
}
/**
* Read in the main image, and scale it to a thumbnail that maintains the
* aspect ratio, but doesn't exceed either of these dimensions.
*/
private void generateThumbnailImage(File mainFile, File thumbFile,
int maxWidth, int maxHeight) throws IOException {
BufferedImage bsrc = ImageIO.read(mainFile);
double scale = Math.min(((double) maxWidth) / bsrc.getWidth(),
((double) maxHeight) / bsrc.getHeight());
AffineTransform at = AffineTransform.getScaleInstance(scale, scale);
int newWidth = (int) (scale * bsrc.getWidth());
int newHeight = (int) (scale * bsrc.getHeight());
updateLog.log("Scaling '" + mainFile + "' by a factor of " + scale
+ ", from " + bsrc.getWidth() + "x" + bsrc.getHeight() + " to "
+ newWidth + "x" + newHeight);
BufferedImage bdest = new BufferedImage(newWidth, newHeight,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = bdest.createGraphics();
g.drawRenderedImage(bsrc, at);
ImageIO.write(bdest, "JPG", thumbFile);
}
}

View file

@ -0,0 +1,78 @@
/* $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.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
/**
* 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, "thumnail");
}
/**
* Check all resources for bogus values on this property.
*/
private void removeNonLiterals(Property prop, String label) {
ResIterator resources = model.listResourcesWithProperty(prop);
try {
while (resources.hasNext()) {
Resource resource = resources.next();
removeNonLiterals(resource, prop, label);
}
} finally {
resources.close();
}
}
/**
* Check this resource for bogus values onthis property.
*/
private void removeNonLiterals(Resource resource, Property prop,
String label) {
List<RDFNode> bogusValues = new ArrayList<RDFNode>();
StmtIterator stmts = resource.listProperties(prop);
try {
while (stmts.hasNext()) {
Statement stmt = stmts.next();
RDFNode object = stmt.getObject();
if (!object.isLiteral()) {
bogusValues.add(object);
}
}
} finally {
stmts.close();
}
for (RDFNode bogusValue : bogusValues) {
updateLog.warn(resource, "discarding " + label
+ " property with non-literal as object: '" + bogusValue
+ "'");
model.createStatement(resource, prop, bogusValue).remove();
}
}
}

View file

@ -0,0 +1,85 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.servlet.setup;
import java.io.File;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.log4j.Logger;
import com.hp.hpl.jena.ontology.OntModel;
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaBaseDao;
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.updater.FileStorageUpdater;
/**
* TODO
*/
public class UpdateUploadedFiles implements ServletContextListener {
private static final Logger log = Logger
.getLogger(UpdateUploadedFiles.class);
/**
* Nothing to do on teardown.
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
return;
}
/**
* Check that the ontology model, the old upload directory, and the file
* storage system are all valid. Then do the update.
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
try {
ServletContext ctx = sce.getServletContext();
WebappDaoFactory wadf = (WebappDaoFactory) ctx
.getAttribute("webappDaoFactory");
if (wadf == null) {
throw new IllegalStateException("Webapp DAO Factory is null");
}
OntModel jenaOntModel = (OntModel) ctx
.getAttribute(JenaBaseDao.JENA_ONT_MODEL_ATTRIBUTE_NAME);
if (jenaOntModel == null) {
throw new IllegalStateException("Ontology model is null");
}
FileStorage fileStorage = (FileStorage) ctx
.getAttribute(FileStorageSetup.ATTRIBUTE_NAME);
if (fileStorage == null) {
throw new IllegalStateException("File storage system is null");
}
String uploadDirectoryName = ConfigurationProperties
.getProperty(FileStorageSetup.PROPERTY_FILE_STORAGE_BASE_DIR);
if (uploadDirectoryName == null) {
throw new IllegalStateException("Upload directory name is null");
}
File uploadDirectory = new File(uploadDirectoryName);
if (!uploadDirectory.exists()) {
throw new IllegalStateException("Upload directory '"
+ uploadDirectory.getAbsolutePath()
+ "' does not exist.");
}
FileStorageUpdater fsu = new FileStorageUpdater(wadf, jenaOntModel,
fileStorage, uploadDirectory);
fsu.update();
} catch (Exception e) {
log.error("Unknown problem", e);
throw new RuntimeException(e);
}
}
}

View file

@ -21,7 +21,6 @@ public class FrontEndEditingUtils {
private static final List<String> VITRO_NS_DATA_PROPS = Arrays.asList(VitroVocabulary.BLURB,
VitroVocabulary.CITATION,
VitroVocabulary.DESCRIPTION,
VitroVocabulary.IMAGETHUMB,
VitroVocabulary.LABEL,
VitroVocabulary.MONIKER
// VitroVocabulary.RDF_TYPE,

View file

@ -108,11 +108,11 @@ public class IndividualView extends ViewObject {
return individual.getKeywords();
}
public String getImageFile() {
return individual.getImageFile();
public String getImageUrl() {
return individual.getImageUrl();
}
public String getImageThumb() {
return individual.getImageThumb();
public String getThumbUrl() {
return individual.getThumbUrl();
}
}

View file

@ -235,7 +235,7 @@ public class PropertyEditLinks extends TagSupport{
if( contains( allowedAccessTypeArray, EditLinkAccess.ADDNEW ) ){
log.debug("vitro namespace property "+propertyUri+" gets an \"add\" link");
LinkStruct ls = null;
if (propertyUri.equals(VitroVocabulary.IMAGETHUMB)) {
if (propertyUri.equals(VitroVocabulary.IND_MAIN_IMAGE)) {
ls = getImageLink(subjectUri, contextPath, "add");
} else {
String url = makeRelativeHref(contextPath +"edit/editDatapropStmtRequestDispatch.jsp",
@ -346,7 +346,7 @@ public class PropertyEditLinks extends TagSupport{
LinkStruct[] links = new LinkStruct[2];
if (predicateUri.equals(VitroVocabulary.IMAGETHUMB)) {
if (predicateUri.equals(VitroVocabulary.IND_MAIN_IMAGE)) {
if( contains( allowedAccessTypeArray, EditLinkAccess.MODIFY ) ){
log.debug("permission found to UPDATE vitro namepsace property statement "+ predicateUri);
links[0] = getImageLink(subjectUri, contextPath, "edit");
@ -643,7 +643,7 @@ public class PropertyEditLinks extends TagSupport{
"entityUri", subjectUri);
ls.setHref(url);
ls.setType(action);
ls.setMouseoverText("upload a new image");
ls.setMouseoverText("upload a new image");
return ls;
}