NIHVIVO-3542 Refactor IndividualController into 8 classes - focus on cleaning the logic in IndividualRequestAnalyzer - create a unit test for IndividualRequestAnalyzer.
This commit is contained in:
parent
5547acc85f
commit
fdee610e22
12 changed files with 1680 additions and 5 deletions
|
@ -0,0 +1,120 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
|
||||
|
||||
/**
|
||||
* Handles requests for entity information.
|
||||
*/
|
||||
public class IndividualController extends FreemarkerHttpServlet {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(IndividualController.class);
|
||||
|
||||
private static final String TEMPLATE_HELP = "individual-help.ftl";
|
||||
|
||||
/**
|
||||
* Use this map to decide which MIME type is suited for the "accept" header.
|
||||
*/
|
||||
public static final Map<String, Float> ACCEPTED_CONTENT_TYPES = initializeContentTypes();
|
||||
private static Map<String, Float> initializeContentTypes() {
|
||||
HashMap<String, Float> map = new HashMap<String, Float>();
|
||||
map.put(HTML_MIMETYPE, 0.5f);
|
||||
map.put(XHTML_MIMETYPE, 0.5f);
|
||||
map.put("application/xml", 0.5f);
|
||||
map.put(RDFXML_MIMETYPE, 1.0f);
|
||||
map.put(N3_MIMETYPE, 1.0f);
|
||||
map.put(TTL_MIMETYPE, 1.0f);
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResponseValues processRequest(VitroRequest vreq) {
|
||||
try {
|
||||
/*
|
||||
* What type of request is this?
|
||||
*/
|
||||
IndividualRequestInfo requestInfo = analyzeTheRequest(vreq);
|
||||
|
||||
switch (requestInfo.getType()) {
|
||||
case RDF_REDIRECT:
|
||||
/*
|
||||
* If someone expects RDF by asking for the individual with an
|
||||
* "accept" HTTP header, redirect them to the preferred URL.
|
||||
*/
|
||||
return new RedirectResponseValues(requestInfo.getRedirectUrl(),
|
||||
HttpServletResponse.SC_SEE_OTHER);
|
||||
case NO_INDIVIDUAL:
|
||||
/*
|
||||
* If we can't figure out what individual you want, or if there
|
||||
* is no such individual, show an informative error page.
|
||||
*/
|
||||
return doNotFound();
|
||||
case BYTESTREAM_REDIRECT:
|
||||
/*
|
||||
* If the Individual requested is a FileBytestream, redirect
|
||||
* them to the direct download URL, so they will get the correct
|
||||
* filename, etc.
|
||||
*/
|
||||
return new RedirectResponseValues(requestInfo.getRedirectUrl(),
|
||||
HttpServletResponse.SC_SEE_OTHER);
|
||||
case LINKED_DATA:
|
||||
/*
|
||||
* If they are asking for RDF using the preferred URL, give it
|
||||
* to them.
|
||||
*/
|
||||
return new IndividualRdfAssembler(vreq,
|
||||
requestInfo.getIndividual(), requestInfo.getRdfFormat())
|
||||
.assembleRdf();
|
||||
default:
|
||||
/*
|
||||
* Otherwise, prepare an HTML response for the requested
|
||||
* individual.
|
||||
*/
|
||||
return new IndividualResponseBuilder(vreq,
|
||||
requestInfo.getIndividual()).assembleResponse();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error(e, e);
|
||||
return new ExceptionResponseValues(e);
|
||||
}
|
||||
}
|
||||
|
||||
private IndividualRequestInfo analyzeTheRequest(VitroRequest vreq) {
|
||||
return new IndividualRequestAnalyzer(vreq,
|
||||
new IndividualRequestAnalysisContextImpl(vreq)).analyze();
|
||||
}
|
||||
|
||||
private ResponseValues doNotFound() {
|
||||
Map<String, Object> body = new HashMap<String, Object>();
|
||||
body.put("title", "Individual Not Found");
|
||||
body.put("errorMessage", "The individual was not found in the system.");
|
||||
|
||||
return new TemplateResponseValues(TEMPLATE_HELP, body,
|
||||
HttpServletResponse.SC_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
doGet(request, response);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.hp.hpl.jena.datatypes.TypeMapper;
|
||||
import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;
|
||||
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.Property;
|
||||
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||
import com.hp.hpl.jena.rdf.model.Resource;
|
||||
import com.hp.hpl.jena.rdf.model.ResourceFactory;
|
||||
import com.hp.hpl.jena.rdf.model.Statement;
|
||||
import com.hp.hpl.jena.rdf.model.StmtIterator;
|
||||
import com.hp.hpl.jena.shared.Lock;
|
||||
import com.hp.hpl.jena.vocabulary.RDF;
|
||||
import com.hp.hpl.jena.vocabulary.RDFS;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RdfResponseValues;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.jena.ExtendedLinkedDataUtils;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.jena.JenaOutputUtils;
|
||||
import edu.cornell.mannlib.vitro.webapp.web.ContentType;
|
||||
|
||||
/**
|
||||
* TODO See where this can be improved.
|
||||
*/
|
||||
public class IndividualRdfAssembler {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(IndividualRdfAssembler.class);
|
||||
|
||||
private static final String RICH_EXPORT_ROOT = "/WEB-INF/rich-export/";
|
||||
private static final String PERSON_CLASS_URI = "http://xmlns.com/foaf/0.1/Person";
|
||||
private static final String INCLUDE_ALL = "all";
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static final Map<String, String> namespaces = new HashMap<String, String>() {{
|
||||
put("display", VitroVocabulary.DISPLAY);
|
||||
put("vitro", VitroVocabulary.vitroURI);
|
||||
put("vitroPublic", VitroVocabulary.VITRO_PUBLIC);
|
||||
}};
|
||||
|
||||
private static final Property extendedLinkedDataProperty = ResourceFactory.createProperty(namespaces.get("vitro") + "extendedLinkedData");
|
||||
private static final Literal xsdTrue = ResourceFactory.createTypedLiteral("true", XSDDatatype.XSDboolean);
|
||||
|
||||
private final VitroRequest vreq;
|
||||
private final ServletContext ctx;
|
||||
private final Individual individual;
|
||||
private final ContentType rdfFormat;
|
||||
|
||||
public IndividualRdfAssembler(VitroRequest vreq, Individual individual,
|
||||
ContentType rdfFormat) {
|
||||
this.vreq = vreq;
|
||||
this.ctx = vreq.getSession().getServletContext();
|
||||
this.individual = individual;
|
||||
this.rdfFormat = rdfFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public ResponseValues assembleRdf() {
|
||||
OntModel ontModel = vreq.getJenaOntModel();
|
||||
|
||||
String[] includes = vreq.getParameterValues("include");
|
||||
Model newModel = getRDF(individual, ontModel, ModelFactory.createDefaultModel(), 0, includes);
|
||||
JenaOutputUtils.setNameSpacePrefixes(newModel, vreq.getWebappDaoFactory());
|
||||
return new RdfResponseValues(rdfFormat, newModel);
|
||||
}
|
||||
|
||||
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth, String[] includes) {
|
||||
|
||||
Resource subj = newModel.getResource(entity.getURI());
|
||||
|
||||
List<DataPropertyStatement> dstates = entity.getDataPropertyStatements();
|
||||
TypeMapper typeMapper = TypeMapper.getInstance();
|
||||
for (DataPropertyStatement ds: dstates) {
|
||||
Property dp = newModel.getProperty(ds.getDatapropURI());
|
||||
Literal lit = null;
|
||||
if ((ds.getLanguage()) != null && (ds.getLanguage().length()>0)) {
|
||||
lit = newModel.createLiteral(ds.getData(),ds.getLanguage());
|
||||
} else if ((ds.getDatatypeURI() != null) && (ds.getDatatypeURI().length()>0)) {
|
||||
lit = newModel.createTypedLiteral(ds.getData(),typeMapper.getSafeTypeByName(ds.getDatatypeURI()));
|
||||
} else {
|
||||
lit = newModel.createLiteral(ds.getData());
|
||||
}
|
||||
newModel.add(newModel.createStatement(subj, dp, lit));
|
||||
}
|
||||
|
||||
if (recurseDepth < 5) {
|
||||
List<ObjectPropertyStatement> ostates = entity.getObjectPropertyStatements();
|
||||
|
||||
for (ObjectPropertyStatement os: ostates) {
|
||||
Property prop = newModel.getProperty(os.getPropertyURI());
|
||||
Resource obj = newModel.getResource(os.getObjectURI());
|
||||
newModel.add(newModel.createStatement(subj, prop, obj));
|
||||
if ( includeInLinkedData(obj, contextModel)) {
|
||||
newModel.add(getRDF(os.getObject(), contextModel, newModel, recurseDepth + 1, includes));
|
||||
} else {
|
||||
contextModel.enterCriticalSection(Lock.READ);
|
||||
try {
|
||||
newModel.add(contextModel.listStatements(obj, RDFS.label, (RDFNode)null));
|
||||
} finally {
|
||||
contextModel.leaveCriticalSection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newModel = getLabelAndTypes(entity, contextModel, newModel );
|
||||
|
||||
// get all the statements not covered by the object property / datatype property code above
|
||||
// note implication that extendedLinkedData individuals will only be evaluated for the
|
||||
// recognized object properties.
|
||||
contextModel.enterCriticalSection(Lock.READ);
|
||||
try {
|
||||
StmtIterator iter = contextModel.listStatements(subj, (Property) null, (RDFNode) null);
|
||||
while (iter.hasNext()) {
|
||||
Statement stmt = iter.next();
|
||||
if (!newModel.contains(stmt)) {
|
||||
newModel.add(stmt);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
contextModel.leaveCriticalSection();
|
||||
}
|
||||
|
||||
if (recurseDepth == 0 && includes != null && entity.isVClass(PERSON_CLASS_URI)) {
|
||||
|
||||
for (String include : includes) {
|
||||
|
||||
String rootDir = null;
|
||||
if (INCLUDE_ALL.equals(include)) {
|
||||
rootDir = RICH_EXPORT_ROOT;
|
||||
} else {
|
||||
rootDir = RICH_EXPORT_ROOT + include + "/";
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
Model extendedModel = ExtendedLinkedDataUtils.createModelFromQueries(ctx, rootDir, contextModel, entity.getURI());
|
||||
long elapsedTimeMillis = System.currentTimeMillis()-start;
|
||||
log.info("Time to create rich export model: msecs = " + elapsedTimeMillis);
|
||||
|
||||
newModel.add(extendedModel);
|
||||
}
|
||||
}
|
||||
|
||||
return newModel;
|
||||
}
|
||||
|
||||
public static boolean includeInLinkedData(Resource object, Model contextModel) {
|
||||
|
||||
boolean retval = false;
|
||||
|
||||
contextModel.enterCriticalSection(Lock.READ);
|
||||
|
||||
try {
|
||||
StmtIterator iter = contextModel.listStatements(object, RDF.type, (RDFNode)null);
|
||||
|
||||
while (iter.hasNext()) {
|
||||
Statement stmt = iter.next();
|
||||
|
||||
if (stmt.getObject().isResource() && contextModel.contains(stmt.getObject().asResource(), extendedLinkedDataProperty, xsdTrue)) {
|
||||
retval = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
contextModel.leaveCriticalSection();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Get the properties that are difficult to get via a filtered WebappDaoFactory. */
|
||||
private Model getLabelAndTypes(Individual entity, Model ontModel, Model newModel){
|
||||
for( VClass vclass : entity.getVClasses()){
|
||||
newModel.add(newModel.getResource(entity.getURI()), RDF.type, newModel.getResource(vclass.getURI()));
|
||||
}
|
||||
|
||||
ontModel.enterCriticalSection(Lock.READ);
|
||||
try {
|
||||
newModel.add(ontModel.listStatements(ontModel.getResource(entity.getURI()), RDFS.label, (RDFNode)null));
|
||||
} finally {
|
||||
ontModel.leaveCriticalSection();
|
||||
}
|
||||
|
||||
return newModel;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
|
||||
/**
|
||||
* Pull the fiddly-bits out of the IndividualRequestAnalyzer to make it easier
|
||||
* to test.
|
||||
*/
|
||||
public interface IndividualRequestAnalysisContext {
|
||||
|
||||
/**
|
||||
* What is the default namespace for the application?
|
||||
*/
|
||||
String getDefaultNamespace();
|
||||
|
||||
/**
|
||||
* Is there a namespace for this prefix? If not, return an empty string, but
|
||||
* never null.
|
||||
*/
|
||||
String getNamespaceForPrefix(String prefix);
|
||||
|
||||
/**
|
||||
* Use the IndividualDao to get this individual.
|
||||
*
|
||||
* If the URI is null, or if no such Individual exists, return null.
|
||||
*/
|
||||
Individual getIndividualByURI(String individualUri);
|
||||
|
||||
/**
|
||||
* If there is a user with this netID, and if they have a profile, return
|
||||
* that Individual. Otherwise, return null.
|
||||
*/
|
||||
Individual getIndividualByNetId(String netId);
|
||||
|
||||
/**
|
||||
* If this Individual represents a File Bytestream, get the Alias URL
|
||||
* associated with it. Otherwise, return null.
|
||||
*/
|
||||
String getAliasUrlForBytestreamIndividual(Individual individual);
|
||||
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.SelfEditingConfiguration;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
|
||||
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapperFactory;
|
||||
|
||||
/**
|
||||
* Implement all of the fiddly-bits that we need for analyzing the request for
|
||||
* an individual, but that we do not want to do in unit tests.
|
||||
*/
|
||||
public class IndividualRequestAnalysisContextImpl implements
|
||||
IndividualRequestAnalysisContext {
|
||||
private final VitroRequest vreq;
|
||||
private final ServletContext ctx;
|
||||
private final WebappDaoFactory wadf;
|
||||
private final IndividualDao iDao;
|
||||
|
||||
public IndividualRequestAnalysisContextImpl(VitroRequest vreq) {
|
||||
this.vreq = vreq;
|
||||
this.ctx = vreq.getSession().getServletContext();
|
||||
this.wadf = vreq.getWebappDaoFactory();
|
||||
this.iDao = wadf.getIndividualDao();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultNamespace() {
|
||||
return wadf.getDefaultNamespace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespaceForPrefix(String prefix) {
|
||||
if (prefix == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
NamespaceMapper namespaceMapper = NamespaceMapperFactory
|
||||
.getNamespaceMapper(ctx);
|
||||
String ns = namespaceMapper.getNamespaceForPrefix(prefix);
|
||||
|
||||
return (ns == null) ? "" : ns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Individual getIndividualByURI(String individualUri) {
|
||||
if (individualUri == null) {
|
||||
return null;
|
||||
}
|
||||
return iDao.getIndividualByURI(individualUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Individual getIndividualByNetId(String netId) {
|
||||
if (netId == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SelfEditingConfiguration sec = SelfEditingConfiguration.getBean(vreq);
|
||||
List<Individual> assocInds = sec.getAssociatedIndividuals(iDao, netId);
|
||||
if (!assocInds.isEmpty()) {
|
||||
return assocInds.get(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAliasUrlForBytestreamIndividual(Individual individual) {
|
||||
if (individual == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
FileInfo fileInfo = FileInfo.instanceFromBytestreamUri(wadf,
|
||||
individual.getURI());
|
||||
if (fileInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fileInfo.getBytestreamAliasUrl();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||
|
||||
import static edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet.N3_MIMETYPE;
|
||||
import static edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet.RDFXML_MIMETYPE;
|
||||
import static edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet.TTL_MIMETYPE;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.web.ContentType;
|
||||
|
||||
/**
|
||||
* All sorts of requests are fielded by the IndividualController. Look at this
|
||||
* request and figure out what type it is, and what data we need in order to
|
||||
* respond.
|
||||
*/
|
||||
public class IndividualRequestAnalyzer {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(IndividualRequestAnalyzer.class);
|
||||
|
||||
|
||||
private static Pattern RDF_REQUEST = Pattern.compile("^/individual/([^/]+)/\\1\\.(rdf|n3|ttl)$");
|
||||
private static Pattern HTML_REQUEST = Pattern.compile("^/display/([^/]+)$");
|
||||
private static Pattern LINKED_DATA_URL = Pattern.compile("^/individual/([^/]+)$");
|
||||
private static Pattern NS_PREFIX_URL = Pattern.compile("^/individual/([^/]*)/([^/]+)$");
|
||||
|
||||
private final VitroRequest vreq;
|
||||
private final IndividualRequestAnalysisContext analysisContext;
|
||||
private final String url;
|
||||
|
||||
public IndividualRequestAnalyzer(VitroRequest vreq,
|
||||
IndividualRequestAnalysisContext analysisContext) {
|
||||
this.vreq = vreq;
|
||||
|
||||
// get URL without hostname or servlet context
|
||||
this.url = vreq.getRequestURI().substring(
|
||||
vreq.getContextPath().length());
|
||||
|
||||
this.analysisContext = analysisContext;
|
||||
}
|
||||
|
||||
public IndividualRequestInfo analyze() {
|
||||
// If this is a request for RDF using an accept header, redirect it to
|
||||
// the preferred URL.
|
||||
String redirectUrl = checkForRedirect();
|
||||
if (redirectUrl != null) {
|
||||
return IndividualRequestInfo.buildRdfRedirectInfo(redirectUrl);
|
||||
}
|
||||
|
||||
// Figure out what individual we are talking about. If we can't figure
|
||||
// it out, complain.
|
||||
Individual individual = getIndividualFromRequest();
|
||||
if (individual == null) {
|
||||
return IndividualRequestInfo.buildNoIndividualInfo();
|
||||
}
|
||||
|
||||
// If the requested individual is a FileBytestream, redirect to its
|
||||
// "alias URL".
|
||||
redirectUrl = getAliasUrlForBytestreamIndividual(individual);
|
||||
if (redirectUrl != null) {
|
||||
return IndividualRequestInfo.buildBytestreamRedirectInfo(redirectUrl);
|
||||
}
|
||||
|
||||
// Check to see whether Linked Data was requested.
|
||||
ContentType rdfFormat = checkUrlForLinkedDataRequest();
|
||||
if (rdfFormat != null) {
|
||||
return IndividualRequestInfo.buildLinkedDataInfo(individual, rdfFormat);
|
||||
}
|
||||
|
||||
// No redirect, no Linked Data; no problem.
|
||||
return IndividualRequestInfo.buildDefaultInfo(individual);
|
||||
}
|
||||
|
||||
/*
|
||||
* Following recipe 3 from
|
||||
* "Best Practice Recipes for Publishing RDF Vocabularies." See
|
||||
* http://www.w3.org/TR/swbp-vocab-pub/#recipe3. The basic idea is that a
|
||||
* URI like http://vivo.cornell.edu/individual/n1234 identifies a real world
|
||||
* individual. HTTP cannot send that as the response to a GET request
|
||||
* because it can only send bytes and not things. The server sends a 303, to
|
||||
* mean "you asked for something I cannot send you, but I can send you this
|
||||
* other stream of bytes about that thing." In the case of a request like
|
||||
* http://vivo.cornell.edu/individual/n1234/n1234.rdf or
|
||||
* http://vivo.cornell.edu/individual/n1234?format=rdfxml, the request is
|
||||
* for a set of bytes rather than an individual, so no 303 is needed.
|
||||
*/
|
||||
private static Pattern URI_PATTERN = Pattern
|
||||
.compile("^/individual/([^/]*)$");
|
||||
|
||||
private String checkForRedirect() {
|
||||
// A "format" parameter is special, and is dealt with elsewhere.
|
||||
String formatParam = getRequestParameter("format", "");
|
||||
if (!formatParam.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Is it "/individual/" followed by a single group?
|
||||
Matcher m = URI_PATTERN.matcher(url);
|
||||
if (!m.matches() || m.groupCount() < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Then, use the "accept" header to decide how to redirect it.
|
||||
ContentType c = checkAcceptHeaderForLinkedDataRequest();
|
||||
if (c != null) {
|
||||
String mediaType = c.getMediaType();
|
||||
if (RDFXML_MIMETYPE.equals(mediaType)) {
|
||||
return "/individual/" + m.group(1) + "/" + m.group(1) + ".rdf";
|
||||
} else if (N3_MIMETYPE.equals(mediaType)) {
|
||||
return "/individual/" + m.group(1) + "/" + m.group(1) + ".n3";
|
||||
} else if (TTL_MIMETYPE.equals(mediaType)) {
|
||||
return "/individual/" + m.group(1) + "/" + m.group(1) + ".ttl";
|
||||
}
|
||||
}
|
||||
// or redirect to the canonical URL for HTML representation.
|
||||
return "/display/" + m.group(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the accept header. This request will trigger a redirect with a 303
|
||||
* ("see also"), because the request is for an individual but the server can
|
||||
* only provide a set of bytes.
|
||||
*/
|
||||
protected ContentType checkAcceptHeaderForLinkedDataRequest() {
|
||||
String acceptHeader = vreq.getHeader("accept");
|
||||
if (acceptHeader == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, Float> typesAndQ = ContentType
|
||||
.getTypesAndQ(acceptHeader);
|
||||
String ctStr = ContentType.getBestContentType(typesAndQ,
|
||||
IndividualController.ACCEPTED_CONTENT_TYPES);
|
||||
|
||||
if (RDFXML_MIMETYPE.equals(ctStr) || N3_MIMETYPE.equals(ctStr)
|
||||
|| TTL_MIMETYPE.equals(ctStr)) {
|
||||
return new ContentType(ctStr);
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
log.error("Problem while checking accept header ", th);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity id from the request. Works for the following styles of
|
||||
* URLs:
|
||||
*
|
||||
* <pre>
|
||||
* /individual?uri=urlencodedURI
|
||||
* /individual?netId=bdc34
|
||||
* /individual?netid=bdc34
|
||||
* /individual/localname
|
||||
* /display/localname
|
||||
* /individual/localname/localname.rdf
|
||||
* /individual/localname/localname.n3
|
||||
* /individual/localname/localname.ttl
|
||||
* /individual/nsprefix/localname
|
||||
* </pre>
|
||||
*
|
||||
* @return null on failure.
|
||||
*/
|
||||
public Individual getIndividualFromRequest() {
|
||||
try {
|
||||
// Check for "uri" parameter.
|
||||
String uri = getRequestParameter("uri", "");
|
||||
if (!uri.isEmpty()) {
|
||||
return getIndividualByUri(uri);
|
||||
}
|
||||
|
||||
// Check for "netId" or "netid" parameter
|
||||
String netId = getRequestParameter("netId",
|
||||
getRequestParameter("netid", ""));
|
||||
if (!netId.isEmpty()) {
|
||||
return getIndividualByNetId(netId);
|
||||
}
|
||||
|
||||
// Check for just a local name
|
||||
Matcher linkedDataMatch = LINKED_DATA_URL.matcher(url);
|
||||
if (linkedDataMatch.matches() && linkedDataMatch.groupCount() == 1) {
|
||||
return getIndividualByLocalname(linkedDataMatch.group(1));
|
||||
}
|
||||
|
||||
// Check for the canonical HTML request.
|
||||
Matcher htmlMatch = HTML_REQUEST.matcher(url);
|
||||
if (htmlMatch.matches() && htmlMatch.groupCount() == 1) {
|
||||
return getIndividualByLocalname(htmlMatch.group(1));
|
||||
}
|
||||
|
||||
// Check for a request for RDF.
|
||||
Matcher rdfMatch = RDF_REQUEST.matcher(url);
|
||||
if (rdfMatch.matches() && rdfMatch.groupCount() == 2) {
|
||||
return getIndividualByLocalname(rdfMatch.group(1));
|
||||
}
|
||||
|
||||
// Does the URL look like a namespace prefix followed by a local
|
||||
// name?
|
||||
Matcher prefix_match = NS_PREFIX_URL.matcher(url);
|
||||
if (prefix_match.matches() && prefix_match.groupCount() == 2) {
|
||||
return getIndividualByPrefixAndLocalname(prefix_match.group(1),
|
||||
prefix_match.group(2));
|
||||
}
|
||||
|
||||
// Couldn't match it to anything.
|
||||
return null;
|
||||
} catch (Throwable e) {
|
||||
log.error("Problems trying to find Individual", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getAliasUrlForBytestreamIndividual(Individual individual) {
|
||||
String aliasUrl = analysisContext.getAliasUrlForBytestreamIndividual(individual);
|
||||
|
||||
if (individual.getURI().equals(aliasUrl)) {
|
||||
// Avoid a tight loop; if the alias URL is equal to the URI,
|
||||
// then don't recognize it as a FileBytestream.
|
||||
return null;
|
||||
} else {
|
||||
return aliasUrl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null if this is not a linked data request, returns content type
|
||||
* if it is a linked data request.
|
||||
*
|
||||
* These are Vitro-specific ways of requesting rdf, unrelated to
|
||||
* semantic web standards. They do not trigger a redirect with a
|
||||
* 303, because the request is for a set of bytes rather than an
|
||||
* individual.
|
||||
*/
|
||||
protected ContentType checkUrlForLinkedDataRequest() {
|
||||
/*
|
||||
* Check for url param specifying format. Example:
|
||||
* http://vivo.cornell.edu/individual/n23?format=rdfxml
|
||||
*/
|
||||
String formatParam = getRequestParameter("format", "");
|
||||
if (formatParam.contains("rdfxml")) {
|
||||
return ContentType.RDFXML;
|
||||
}
|
||||
if (formatParam.contains("n3")) {
|
||||
return ContentType.N3;
|
||||
}
|
||||
if (formatParam.contains("ttl")) {
|
||||
return ContentType.TURTLE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for parts of URL that indicate request for RDF. Examples:
|
||||
* http://vivo.cornell.edu/individual/n23/n23.rdf
|
||||
* http://vivo.cornell.edu/individual/n23/n23.n3
|
||||
* http://vivo.cornell.edu/individual/n23/n23.ttl
|
||||
*/
|
||||
Matcher rdfMatch = RDF_REQUEST.matcher(url);
|
||||
if (rdfMatch.matches() && rdfMatch.groupCount() == 2) {
|
||||
String rdfType = rdfMatch.group(2);
|
||||
if ("rdf".equals(rdfType)) {
|
||||
return ContentType.RDFXML;
|
||||
}
|
||||
if ("n3".equals(rdfType)) {
|
||||
return ContentType.N3;
|
||||
}
|
||||
if ("ttl".equals(rdfType)) {
|
||||
return ContentType.TURTLE;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String getRequestParameter(String key, String defaultValue) {
|
||||
String value = vreq.getParameter(key);
|
||||
if ((value == null) || value.isEmpty()) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private Individual getIndividualByUri(String uri) {
|
||||
return analysisContext.getIndividualByURI(uri);
|
||||
}
|
||||
|
||||
private Individual getIndividualByLocalname(String localname) {
|
||||
String defaultNamespace = analysisContext.getDefaultNamespace();
|
||||
String uri = defaultNamespace + localname;
|
||||
return getIndividualByUri(uri);
|
||||
}
|
||||
|
||||
private Individual getIndividualByPrefixAndLocalname(String prefix,
|
||||
String localName) {
|
||||
String ns = analysisContext.getNamespaceForPrefix(prefix);
|
||||
return getIndividualByUri(ns + localName);
|
||||
}
|
||||
|
||||
private Individual getIndividualByNetId(String netId) {
|
||||
return analysisContext.getIndividualByNetId(netId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
import edu.cornell.mannlib.vitro.webapp.web.ContentType;
|
||||
|
||||
/**
|
||||
* All sorts of requests are fielded by the IndividualController. This is the
|
||||
* essence of such a the request.
|
||||
*/
|
||||
public class IndividualRequestInfo {
|
||||
public enum Type {
|
||||
RDF_REDIRECT, // Redirect a requst for RDF to the preferred URL.
|
||||
BYTESTREAM_REDIRECT, // Redirect a request for file contents.
|
||||
NO_INDIVIDUAL, // The requested individual doesn't exist.
|
||||
LINKED_DATA, // Requesting RDF for this individual.
|
||||
DEFAULT // Requesting HTML response for this individual.
|
||||
}
|
||||
|
||||
public static IndividualRequestInfo buildRdfRedirectInfo(String redirectUrl) {
|
||||
return new IndividualRequestInfo(Type.RDF_REDIRECT, redirectUrl, null,
|
||||
null);
|
||||
}
|
||||
|
||||
public static IndividualRequestInfo buildBytestreamRedirectInfo(
|
||||
String redirectUrl) {
|
||||
return new IndividualRequestInfo(Type.BYTESTREAM_REDIRECT, redirectUrl,
|
||||
null, null);
|
||||
}
|
||||
|
||||
public static IndividualRequestInfo buildNoIndividualInfo() {
|
||||
return new IndividualRequestInfo(Type.NO_INDIVIDUAL, null, null, null);
|
||||
}
|
||||
|
||||
public static IndividualRequestInfo buildLinkedDataInfo(
|
||||
Individual individual, ContentType rdfFormat) {
|
||||
return new IndividualRequestInfo(Type.LINKED_DATA, null, individual,
|
||||
rdfFormat);
|
||||
}
|
||||
|
||||
public static IndividualRequestInfo buildDefaultInfo(Individual individual) {
|
||||
return new IndividualRequestInfo(Type.DEFAULT, null, individual, null);
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
private final String redirectUrl;
|
||||
private final Individual individual;
|
||||
private final ContentType rdfFormat;
|
||||
|
||||
private IndividualRequestInfo(Type type, String redirectUrl,
|
||||
Individual individual, ContentType rdfFormat) {
|
||||
if (type == null) {
|
||||
throw new NullPointerException("type may not be null.");
|
||||
}
|
||||
|
||||
if (((type == Type.RDF_REDIRECT) || (type == Type.BYTESTREAM_REDIRECT))
|
||||
&& (redirectUrl == null)) {
|
||||
throw new NullPointerException(
|
||||
"redirectUrl may not be null if type is " + type + ".");
|
||||
}
|
||||
|
||||
if (((type == Type.LINKED_DATA) || (type == Type.DEFAULT))
|
||||
&& (individual == null)) {
|
||||
throw new NullPointerException(
|
||||
"individual may not be null if type is " + type + ".");
|
||||
}
|
||||
|
||||
if ((type == Type.LINKED_DATA) && (rdfFormat == null)) {
|
||||
throw new NullPointerException(
|
||||
"rdfFormat may not be null if type is " + type + ".");
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
this.redirectUrl = redirectUrl;
|
||||
this.individual = individual;
|
||||
this.rdfFormat = rdfFormat;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public String getRedirectUrl() {
|
||||
return this.redirectUrl;
|
||||
}
|
||||
|
||||
public Individual getIndividual() {
|
||||
return this.individual;
|
||||
}
|
||||
|
||||
public ContentType getRdfFormat() {
|
||||
return this.rdfFormat;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
|
||||
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
|
||||
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.DisplayVocabulary;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||
import edu.cornell.mannlib.vitro.webapp.web.beanswrappers.ReadOnlyBeansWrapper;
|
||||
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.IndividualTemplateModel;
|
||||
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individuallist.ListedIndividual;
|
||||
import freemarker.ext.beans.BeansWrapper;
|
||||
import freemarker.template.TemplateModel;
|
||||
import freemarker.template.TemplateModelException;
|
||||
|
||||
/**
|
||||
* We have determined that the request is for a normal Individual, and needs an
|
||||
* HTML response. Assemble the information for that response.
|
||||
*
|
||||
* TODO clean this up.
|
||||
*/
|
||||
class IndividualResponseBuilder {
|
||||
private static final Map<String, String> namespaces = new HashMap<String, String>() {{
|
||||
put("display", VitroVocabulary.DISPLAY);
|
||||
put("vitro", VitroVocabulary.vitroURI);
|
||||
put("vitroPublic", VitroVocabulary.VITRO_PUBLIC);
|
||||
}};
|
||||
|
||||
private final VitroRequest vreq;
|
||||
private final WebappDaoFactory wadf;
|
||||
private final IndividualDao iDao;
|
||||
private final ObjectPropertyDao opDao;
|
||||
|
||||
private final Individual individual;
|
||||
|
||||
public IndividualResponseBuilder(VitroRequest vreq, Individual individual) {
|
||||
this.vreq = vreq;
|
||||
this.wadf = vreq.getWebappDaoFactory();
|
||||
this.iDao = wadf.getIndividualDao();
|
||||
this.opDao = wadf.getObjectPropertyDao();
|
||||
|
||||
this.individual = individual;
|
||||
}
|
||||
|
||||
ResponseValues assembleResponse() throws TemplateModelException {
|
||||
Map<String, Object> body = new HashMap<String, Object>();
|
||||
|
||||
body.put("title", individual.getName());
|
||||
body.put("relatedSubject", getRelatedSubject());
|
||||
body.put("namespaces", namespaces);
|
||||
body.put("temporalVisualizationEnabled", getTemporalVisualizationFlag());
|
||||
body.put("verbosePropertySwitch", getVerbosePropertyValues());
|
||||
|
||||
IndividualTemplateModel itm = getIndividualTemplateModel(individual);
|
||||
/* We need to expose non-getters in displaying the individual's property list,
|
||||
* since it requires calls to methods with parameters.
|
||||
* This is still safe, because we are only putting BaseTemplateModel objects
|
||||
* into the data model: no real data can be modified.
|
||||
*/
|
||||
// body.put("individual", wrap(itm, BeansWrapper.EXPOSE_SAFE));
|
||||
body.put("individual", wrap(itm, new ReadOnlyBeansWrapper()));
|
||||
|
||||
body.put("headContent", getRdfLinkTag(itm));
|
||||
|
||||
//If special values required for individuals like menu, include values in template values
|
||||
body.putAll(getSpecialEditingValues());
|
||||
|
||||
String template = new IndividualTemplateLocator(vreq, individual).findTemplate();
|
||||
|
||||
return new TemplateResponseValues(template, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a "relatedSubjectUri" parameter has been supplied, and, if so,
|
||||
* retrieve the related individual.
|
||||
*
|
||||
* Some individuals make little sense standing alone and should be displayed
|
||||
* in the context of their relationship to another.
|
||||
*/
|
||||
private Map<String, Object> getRelatedSubject() {
|
||||
Map<String, Object> map = null;
|
||||
|
||||
String relatedSubjectUri = vreq.getParameter("relatedSubjectUri");
|
||||
if (relatedSubjectUri != null) {
|
||||
Individual relatedSubjectInd = iDao.getIndividualByURI(relatedSubjectUri);
|
||||
if (relatedSubjectInd != null) {
|
||||
map = new HashMap<String, Object>();
|
||||
map.put("name", relatedSubjectInd.getName());
|
||||
|
||||
// TODO find out which of these values is the correct one
|
||||
map.put("url", UrlBuilder.getIndividualProfileUrl(relatedSubjectInd, vreq));
|
||||
map.put("url", (new ListedIndividual(relatedSubjectInd, vreq)).getProfileUrl());
|
||||
|
||||
String relatingPredicateUri = vreq.getParameter("relatingPredicateUri");
|
||||
if (relatingPredicateUri != null) {
|
||||
ObjectProperty relatingPredicateProp = opDao.getObjectPropertyByURI(relatingPredicateUri);
|
||||
if (relatingPredicateProp != null) {
|
||||
map.put("relatingPredicateDomainPublic", relatingPredicateProp.getDomainPublic());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private boolean getTemporalVisualizationFlag() {
|
||||
String property = ConfigurationProperties.getBean(vreq).getProperty(
|
||||
"visualization.temporal");
|
||||
return "enabled".equals(property);
|
||||
}
|
||||
|
||||
private Map<String, Object> getVerbosePropertyValues() {
|
||||
Map<String, Object> map = null;
|
||||
|
||||
if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.SEE_VERBOSE_PROPERTY_INFORMATION.ACTIONS)) {
|
||||
// Get current verbose property display value
|
||||
String verbose = vreq.getParameter("verbose");
|
||||
Boolean verboseValue;
|
||||
// If the form was submitted, get that value
|
||||
if (verbose != null) {
|
||||
verboseValue = "true".equals(verbose);
|
||||
// If form not submitted, get the session value
|
||||
} else {
|
||||
Boolean verbosePropertyDisplayValueInSession = (Boolean) vreq.getSession().getAttribute("verbosePropertyDisplay");
|
||||
// True if session value is true, otherwise (session value is false or null) false
|
||||
verboseValue = Boolean.TRUE.equals(verbosePropertyDisplayValueInSession);
|
||||
}
|
||||
vreq.getSession().setAttribute("verbosePropertyDisplay", verboseValue);
|
||||
|
||||
map = new HashMap<String, Object>();
|
||||
map.put("currentValue", verboseValue);
|
||||
|
||||
/* Factors contributing to switching from a form to an anchor element:
|
||||
- Can't use GET with a query string on the action unless there is no form data, since
|
||||
the form data is appended to the action with a "?", so there can't already be a query string
|
||||
on it.
|
||||
- The browser (at least Firefox) does not submit a form that has no form data.
|
||||
- Some browsers might strip the query string off the form action of a POST - though
|
||||
probably they shouldn't, because the HTML spec allows a full URI as a form action.
|
||||
- Given these three, the only reliable solution is to dynamically create hidden inputs
|
||||
for the query parameters.
|
||||
- Much simpler is to just create an anchor element. This has the added advantage that the
|
||||
browser doesn't ask to resend the form data when reloading the page.
|
||||
*/
|
||||
String url = vreq.getRequestURI() + "?verbose=" + !verboseValue;
|
||||
// Append request query string, except for current verbose value, to url
|
||||
String queryString = vreq.getQueryString();
|
||||
if (queryString != null) {
|
||||
String[] params = queryString.split("&");
|
||||
for (String param : params) {
|
||||
if (! param.startsWith("verbose=")) {
|
||||
url += "&" + param;
|
||||
}
|
||||
}
|
||||
}
|
||||
map.put("url", url);
|
||||
} else {
|
||||
vreq.getSession().setAttribute("verbosePropertyDisplay", false);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private IndividualTemplateModel getIndividualTemplateModel(
|
||||
Individual individual) {
|
||||
individual.sortForDisplay();
|
||||
return new IndividualTemplateModel(individual, vreq);
|
||||
}
|
||||
|
||||
private TemplateModel wrap(Object obj, BeansWrapper wrapper) throws TemplateModelException {
|
||||
return wrapper.wrap(obj);
|
||||
}
|
||||
|
||||
private String getRdfLinkTag(IndividualTemplateModel itm) {
|
||||
String linkTag = null;
|
||||
String linkedDataUrl = itm.getRdfUrl();
|
||||
if (linkedDataUrl != null) {
|
||||
linkTag = "<link rel=\"alternate\" type=\"application/rdf+xml\" href=\"" +
|
||||
linkedDataUrl + "\" /> ";
|
||||
}
|
||||
return linkTag;
|
||||
}
|
||||
|
||||
//Get special values for cases such as Menu Management editing
|
||||
private Map<String, Object> getSpecialEditingValues() {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
if(vreq.getAttribute(VitroRequest.SPECIAL_WRITE_MODEL) != null) {
|
||||
map.put("reorderUrl", UrlBuilder.getUrl(DisplayVocabulary.REORDER_MENU_URL));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.VClassDao;
|
||||
import edu.cornell.mannlib.vitro.webapp.reasoner.SimpleReasoner;
|
||||
|
||||
/**
|
||||
* Figure out which Freemarker template to use when displaying this individual.
|
||||
*
|
||||
* If one of the classes of the individual (or one of the superclasses) has a
|
||||
* custom template associated with it, use the first one that we find.
|
||||
* Otherwise, use the default template.
|
||||
*
|
||||
* TODO examine the logic in this class. Is there anything we can get rid of?
|
||||
*/
|
||||
class IndividualTemplateLocator {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(IndividualTemplateLocator.class);
|
||||
|
||||
private static final String TEMPLATE_INDIVIDUAL_DEFAULT = "individual.ftl";
|
||||
|
||||
private final VitroRequest vreq;
|
||||
private final ServletContext ctx;
|
||||
|
||||
private final Individual individual;
|
||||
|
||||
public IndividualTemplateLocator(VitroRequest vreq, Individual individual) {
|
||||
this.vreq = vreq;
|
||||
this.ctx = vreq.getSession().getServletContext();
|
||||
|
||||
this.individual = individual;
|
||||
}
|
||||
|
||||
// Determine whether the individual has a custom display template based on its class membership.
|
||||
// If not, return the default individual template.
|
||||
String findTemplate() {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
String vclassName = "unknown";
|
||||
String customTemplate = null;
|
||||
|
||||
// First check vclass
|
||||
if( individual.getVClass() != null ){
|
||||
vclassName = individual.getVClass().getName();
|
||||
List<VClass> directClasses = individual.getVClasses(true);
|
||||
for (VClass vclass : directClasses) {
|
||||
customTemplate = vclass.getCustomDisplayView();
|
||||
if (customTemplate != null) {
|
||||
if (customTemplate.length()>0) {
|
||||
vclassName = vclass.getName(); // reset entity vclassname to name of class where a custom view; this call has side-effects
|
||||
log.debug("Found direct class [" + vclass.getName() + "] with custom view " + customTemplate + "; resetting entity vclassName to this class");
|
||||
break;
|
||||
} else {
|
||||
customTemplate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If no custom template defined, check other vclasses
|
||||
if (customTemplate == null) {
|
||||
List<VClass> inferredClasses = individual.getVClasses(false);
|
||||
for (VClass vclass : inferredClasses) {
|
||||
customTemplate = vclass.getCustomDisplayView();
|
||||
if (customTemplate != null) {
|
||||
if (customTemplate.length()>0) {
|
||||
// note that NOT changing entity vclassName here yet
|
||||
log.debug("Found inferred class [" + vclass.getName() + "] with custom view " + customTemplate);
|
||||
break;
|
||||
} else {
|
||||
customTemplate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If still no custom template defined, and inferencing is asynchronous (under RDB), check
|
||||
// the superclasses of the vclass for a custom template specification.
|
||||
SimpleReasoner simpleReasoner = (SimpleReasoner) ctx.getAttribute(SimpleReasoner.class.getName());
|
||||
if (customTemplate == null && simpleReasoner != null && simpleReasoner.isABoxReasoningAsynchronous()) {
|
||||
log.debug("Checking superclasses for custom template specification because ABox reasoning is asynchronous");
|
||||
for (VClass directVClass : directClasses) {
|
||||
VClassDao vcDao = vreq.getWebappDaoFactory().getVClassDao();
|
||||
List<String> superClassUris = vcDao.getAllSuperClassURIs(directVClass.getURI());
|
||||
for (String uri : superClassUris) {
|
||||
VClass vclass = vcDao.getVClassByURI(uri);
|
||||
customTemplate = vclass.getCustomDisplayView();
|
||||
if (customTemplate != null) {
|
||||
if (customTemplate.length()>0) {
|
||||
// note that NOT changing entity vclassName here
|
||||
log.debug("Found superclass [" + vclass.getName() + "] with custom view " + customTemplate);
|
||||
break;
|
||||
} else {
|
||||
customTemplate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (individual.getVClassURI() != null) {
|
||||
log.debug("Individual " + individual.getURI() + " with class URI " +
|
||||
individual.getVClassURI() + ": no class found with that URI");
|
||||
}
|
||||
|
||||
return customTemplate != null ? customTemplate : TEMPLATE_INDIVIDUAL_DEFAULT;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,480 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.controller.individual;
|
||||
|
||||
import static edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet.N3_MIMETYPE;
|
||||
import static edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet.RDFXML_MIMETYPE;
|
||||
import static edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet.TTL_MIMETYPE;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import stubs.edu.cornell.mannlib.vitro.webapp.beans.IndividualStub;
|
||||
import stubs.javax.servlet.http.HttpServletRequestStub;
|
||||
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.web.ContentType;
|
||||
|
||||
/**
|
||||
* Are we able to figure out what sort of Individual request this is?
|
||||
*/
|
||||
public class IndividualRequestAnalyzerTest extends AbstractTestClass {
|
||||
|
||||
/**
|
||||
* Info about the application.
|
||||
*/
|
||||
private static final String URL_HOME_PAGE = "http://vivo.mydomain.edu";
|
||||
private static final String URL_INDIVIDUAL_PAGE = URL_HOME_PAGE
|
||||
+ "/individual";
|
||||
private static final String DEFAULT_NAMESPACE = URL_INDIVIDUAL_PAGE + "/";
|
||||
|
||||
/**
|
||||
* Info about the individual that we're testing (mostly).
|
||||
*/
|
||||
private static final String ID_INDIVIDUAL_TEST = "testId";
|
||||
private static final String URI_INDIVIDUAL_TEST = DEFAULT_NAMESPACE
|
||||
+ ID_INDIVIDUAL_TEST;
|
||||
private static final String NETID_USER_TEST = "joeUser";
|
||||
|
||||
/**
|
||||
* Info about the file bytestream that appears in one test.
|
||||
*/
|
||||
/** The ID of an Individual that represents a FileBytestream object. */
|
||||
private static final String ID_FILE_BYTESTREAM = "bytesId";
|
||||
private static final String URI_FILE_BYTESTREAM = DEFAULT_NAMESPACE
|
||||
+ ID_FILE_BYTESTREAM;
|
||||
private static final String BYTESTREAM_FILENAME = "imageFilename.jpg";
|
||||
private static final String URL_BYTESTREAM_ALIAS = URL_HOME_PAGE + "/file/"
|
||||
+ ID_FILE_BYTESTREAM + "/" + BYTESTREAM_FILENAME;
|
||||
|
||||
/**
|
||||
* Info about an individual that appears in a different namespace.
|
||||
*/
|
||||
private static final String SOME_PREFIX = "somePrefix";
|
||||
private static final String SOME_NAMESPACE = "http://some.namespace/";
|
||||
private static final String ID_INDIVIDUAL_FOREIGN = "foreignId";
|
||||
private static final String URI_INDIVIDUAL_FOREIGN = SOME_NAMESPACE
|
||||
+ ID_INDIVIDUAL_FOREIGN;
|
||||
|
||||
private IndividualRequestAnalyzer analyzer;
|
||||
private MyAnalysisContext analysisContext;
|
||||
private HttpServletRequestStub req;
|
||||
private VitroRequest vreq;
|
||||
private IndividualRequestInfo requestInfo;
|
||||
|
||||
private IndividualStub testIndividual;
|
||||
private IndividualStub bytestreamIndividual;
|
||||
private IndividualStub foreignIndividual;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
req = new HttpServletRequestStub();
|
||||
analysisContext = new MyAnalysisContext(DEFAULT_NAMESPACE);
|
||||
|
||||
testIndividual = new IndividualStub(URI_INDIVIDUAL_TEST);
|
||||
analysisContext.addIndividual(testIndividual);
|
||||
analysisContext.addProfilePage(NETID_USER_TEST, testIndividual);
|
||||
|
||||
bytestreamIndividual = new IndividualStub(URI_FILE_BYTESTREAM);
|
||||
analysisContext.addIndividual(bytestreamIndividual);
|
||||
analysisContext.setAliasUrl(URI_FILE_BYTESTREAM, URL_BYTESTREAM_ALIAS);
|
||||
|
||||
foreignIndividual = new IndividualStub(URI_INDIVIDUAL_FOREIGN);
|
||||
analysisContext.addIndividual(foreignIndividual);
|
||||
analysisContext.setNamespacePrefix(SOME_PREFIX, SOME_NAMESPACE);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests - locate by parameter
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** /individual?uri=urlencodedURI */
|
||||
@Test
|
||||
public void findIndividualByUriParameter() {
|
||||
req.setRequestUrl(url(URL_INDIVIDUAL_PAGE));
|
||||
req.addParameter("uri", URI_INDIVIDUAL_TEST);
|
||||
analyzeIt();
|
||||
assertDefaultRequestInfo("find by URI parameter", URI_INDIVIDUAL_TEST);
|
||||
}
|
||||
|
||||
/** /individual?netId=bdc34 */
|
||||
@Test
|
||||
public void findIndividualByNetIdParameter() {
|
||||
req.setRequestUrl(url(URL_INDIVIDUAL_PAGE));
|
||||
req.addParameter("netId", NETID_USER_TEST);
|
||||
analyzeIt();
|
||||
assertDefaultRequestInfo("find by netId parameter", URI_INDIVIDUAL_TEST);
|
||||
}
|
||||
|
||||
/** /individual?netid=bdc34 */
|
||||
@Test
|
||||
public void findIndividualByNetidParameter() {
|
||||
req.setRequestUrl(url(URL_INDIVIDUAL_PAGE));
|
||||
req.addParameter("netid", NETID_USER_TEST);
|
||||
analyzeIt();
|
||||
assertDefaultRequestInfo("find by netid parameter", URI_INDIVIDUAL_TEST);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests - miscellaneous
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** /display/localname */
|
||||
@Test
|
||||
public void findIndividualByDisplayPath() {
|
||||
req.setRequestUrl(url(URL_HOME_PAGE + "/display/" + ID_INDIVIDUAL_TEST));
|
||||
analyzeIt();
|
||||
assertDefaultRequestInfo("find by display path", URI_INDIVIDUAL_TEST);
|
||||
}
|
||||
|
||||
/** /individual/nsPrefix/localname */
|
||||
@Test
|
||||
public void findByPrefixAndLocalname() {
|
||||
req.setRequestUrl(url(DEFAULT_NAMESPACE + SOME_PREFIX + "/"
|
||||
+ ID_INDIVIDUAL_FOREIGN));
|
||||
analyzeIt();
|
||||
assertDefaultRequestInfo("find by prefix and localname",
|
||||
URI_INDIVIDUAL_FOREIGN);
|
||||
}
|
||||
|
||||
/** /individual/a/b/c fails. */
|
||||
@Test
|
||||
public void unrecognizedPath() {
|
||||
req.setRequestUrl(url(DEFAULT_NAMESPACE + "this/that/theOther"));
|
||||
analyzeIt();
|
||||
assertNoIndividualRequestInfo("unrecognized path");
|
||||
}
|
||||
|
||||
/** /display/localname but no such individual */
|
||||
@Test
|
||||
public void findNoSuchIndividualByDisplayPath() {
|
||||
req.setRequestUrl(url(URL_HOME_PAGE + "/display/" + "bogusID"));
|
||||
analyzeIt();
|
||||
assertNoIndividualRequestInfo("unrecognized ID");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests - redirect a FileBytestream
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void redirectAFileBytestreamIndividual() {
|
||||
req.setRequestUrl(url(URL_HOME_PAGE + "/display/" + ID_FILE_BYTESTREAM));
|
||||
analyzeIt();
|
||||
assertBytestreamRedirectInfo("bytestream redirect",
|
||||
URL_BYTESTREAM_ALIAS);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests - redirect from a Linked Data path
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** /individual/localname, accept=RDF redirects to /individual/id/id.rdf */
|
||||
@Test
|
||||
public void redirectFromLinkedDataPathAcceptRdf() {
|
||||
req.setRequestUrl(url(DEFAULT_NAMESPACE + ID_INDIVIDUAL_TEST));
|
||||
req.setHeader("accept", RDFXML_MIMETYPE);
|
||||
analyzeIt();
|
||||
assertRdfRedirectRequestInfo("by linked data path, accept RDF",
|
||||
redirectUrlForRdfStream(ID_INDIVIDUAL_TEST, ".rdf"));
|
||||
}
|
||||
|
||||
/** /individual/localname, accept=N3 redirects to /individual/id/id.n3 */
|
||||
@Test
|
||||
public void redirectFromLinkedDataPathAcceptN3() {
|
||||
req.setRequestUrl(url(DEFAULT_NAMESPACE + ID_INDIVIDUAL_TEST));
|
||||
req.setHeader("accept", N3_MIMETYPE);
|
||||
analyzeIt();
|
||||
assertRdfRedirectRequestInfo("by linked data path, accept N3",
|
||||
redirectUrlForRdfStream(ID_INDIVIDUAL_TEST, ".n3"));
|
||||
}
|
||||
|
||||
/** /individual/localname, accept=TTL redirects to /individual/id/id.ttl */
|
||||
@Test
|
||||
public void redirectFromLinkedDataPathAcceptTurtle() {
|
||||
req.setRequestUrl(url(DEFAULT_NAMESPACE + ID_INDIVIDUAL_TEST));
|
||||
req.setHeader("accept", TTL_MIMETYPE);
|
||||
analyzeIt();
|
||||
assertRdfRedirectRequestInfo("by linked data path, accept TTL",
|
||||
redirectUrlForRdfStream(ID_INDIVIDUAL_TEST, ".ttl"));
|
||||
}
|
||||
|
||||
/** /individual/localname, no accept, redirects to /display/id */
|
||||
@Test
|
||||
public void redirectFromLinkedDataPathNoAccept() {
|
||||
req.setRequestUrl(url(DEFAULT_NAMESPACE + ID_INDIVIDUAL_TEST));
|
||||
analyzeIt();
|
||||
assertRdfRedirectRequestInfo("by linked data path with no accept",
|
||||
"/display/" + ID_INDIVIDUAL_TEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the accept header is set to a recognized value, but not one of the
|
||||
* onese that we like, treat the same as no accept.
|
||||
*/
|
||||
@Test
|
||||
public void redirectFromLinkedDataPathAcceptStrange() {
|
||||
req.setRequestUrl(url(DEFAULT_NAMESPACE + ID_INDIVIDUAL_TEST));
|
||||
req.setHeader("accept", "application/json");
|
||||
analyzeIt();
|
||||
assertRdfRedirectRequestInfo(
|
||||
"by linked data path, accept a strange content type",
|
||||
"/display/" + ID_INDIVIDUAL_TEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the accept header is set to an unrecognized value, treat the same as
|
||||
* no accept.
|
||||
*/
|
||||
@Test
|
||||
public void redirectFromLinkedDataPathAcceptGarbage() {
|
||||
req.setRequestUrl(url(DEFAULT_NAMESPACE + ID_INDIVIDUAL_TEST));
|
||||
req.setHeader("accept", "a/b/c");
|
||||
analyzeIt();
|
||||
assertRdfRedirectRequestInfo(
|
||||
"by linked data path, accept an unrecognized content type",
|
||||
"/display/" + ID_INDIVIDUAL_TEST);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests - satisfy requests for RDF formats.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void getRdfByUriAndFormatParameters() {
|
||||
req.setRequestUrl(url(URL_INDIVIDUAL_PAGE));
|
||||
req.addParameter("uri", URI_INDIVIDUAL_TEST);
|
||||
req.addParameter("format", "rdfxml");
|
||||
analyzeIt();
|
||||
assertLinkedDataRequestInfo("RDF by uri and format parameters",
|
||||
URI_INDIVIDUAL_TEST, ContentType.RDFXML);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getN3ByUriAndFormatParameters() {
|
||||
req.setRequestUrl(url(URL_INDIVIDUAL_PAGE));
|
||||
req.addParameter("uri", URI_INDIVIDUAL_TEST);
|
||||
req.addParameter("format", "n3");
|
||||
analyzeIt();
|
||||
assertLinkedDataRequestInfo("N3 by uri and format parameters",
|
||||
URI_INDIVIDUAL_TEST, ContentType.N3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTurtleByUriAndFormatParameters() {
|
||||
req.setRequestUrl(url(URL_INDIVIDUAL_PAGE));
|
||||
req.addParameter("uri", URI_INDIVIDUAL_TEST);
|
||||
req.addParameter("format", "ttl");
|
||||
analyzeIt();
|
||||
assertLinkedDataRequestInfo("Turtle by uri and format parameters",
|
||||
URI_INDIVIDUAL_TEST, ContentType.TURTLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unrecognizedFormatParameter() {
|
||||
req.setRequestUrl(url(URL_INDIVIDUAL_PAGE));
|
||||
req.addParameter("uri", URI_INDIVIDUAL_TEST);
|
||||
req.addParameter("format", "bogus");
|
||||
analyzeIt();
|
||||
assertDefaultRequestInfo("unrecognized format means HTML response",
|
||||
URI_INDIVIDUAL_TEST);
|
||||
}
|
||||
|
||||
/** http://vivo.cornell.edu/individual/n23/n23.rdf */
|
||||
@Test
|
||||
public void getRdfByStreamRequest() {
|
||||
req.setRequestUrl(absoluteUrlForRdfStream(ID_INDIVIDUAL_TEST, ".rdf"));
|
||||
analyzeIt();
|
||||
assertLinkedDataRequestInfo("RDF by stream request",
|
||||
URI_INDIVIDUAL_TEST, ContentType.RDFXML);
|
||||
}
|
||||
|
||||
/** http://vivo.cornell.edu/individual/n23/n23.n3 */
|
||||
@Test
|
||||
public void getN3ByStreamRequest() {
|
||||
req.setRequestUrl(absoluteUrlForRdfStream(ID_INDIVIDUAL_TEST, ".n3"));
|
||||
analyzeIt();
|
||||
assertLinkedDataRequestInfo("N3 by stream request",
|
||||
URI_INDIVIDUAL_TEST, ContentType.N3);
|
||||
}
|
||||
|
||||
/** http://vivo.cornell.edu/individual/n23/n23.rdf */
|
||||
@Test
|
||||
public void getTurtleByStreamRequest() {
|
||||
req.setRequestUrl(absoluteUrlForRdfStream(ID_INDIVIDUAL_TEST, ".ttl"));
|
||||
analyzeIt();
|
||||
assertLinkedDataRequestInfo("Turtle by stream request",
|
||||
URI_INDIVIDUAL_TEST, ContentType.TURTLE);
|
||||
}
|
||||
|
||||
/** http://vivo.cornell.edu/individual/n23/n23.bogus is an error */
|
||||
@Test
|
||||
public void unrecognizedFormatForRdfStreamRequest() {
|
||||
req.setRequestUrl(absoluteUrlForRdfStream(ID_INDIVIDUAL_TEST, ".bogus"));
|
||||
analyzeIt();
|
||||
assertNoIndividualRequestInfo("Unrecognized RDF stream request");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** /individual/n23/n23.rdf, or the like */
|
||||
private String redirectUrlForRdfStream(String id, String extension) {
|
||||
return "/individual/" + id + "/" + id + extension;
|
||||
}
|
||||
|
||||
/** http://vivo.mydomain.edu/individual/n23/n23.rdf, or the like */
|
||||
private URL absoluteUrlForRdfStream(String id, String extension) {
|
||||
return url(DEFAULT_NAMESPACE + id + "/" + id + extension);
|
||||
}
|
||||
|
||||
private void analyzeIt() {
|
||||
vreq = new VitroRequest(req);
|
||||
analyzer = new IndividualRequestAnalyzer(vreq, analysisContext);
|
||||
requestInfo = analyzer.analyze();
|
||||
}
|
||||
|
||||
/** We should have a DEFAULT request with the expected Individual. */
|
||||
private void assertDefaultRequestInfo(String message, String individualUri) {
|
||||
assertEquals(message + ": expecting DEFAULT request type",
|
||||
IndividualRequestInfo.Type.DEFAULT, requestInfo.getType());
|
||||
assertNotNull(message + ": expected an individual",
|
||||
requestInfo.getIndividual());
|
||||
assertEquals(message + ": expected individual", individualUri,
|
||||
requestInfo.getIndividual().getURI());
|
||||
}
|
||||
|
||||
/** We should have a RDF_REDIRECT request with the expected URL. */
|
||||
private void assertRdfRedirectRequestInfo(String message, String redirectUrl) {
|
||||
assertEquals(message + ": expecting RDF_REDIRECT request type",
|
||||
IndividualRequestInfo.Type.RDF_REDIRECT, requestInfo.getType());
|
||||
assertEquals(message + ": expected redirect URL", redirectUrl,
|
||||
requestInfo.getRedirectUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* We should have a BYTESTREAM_REDIRECT request with the expected Individual
|
||||
* and alias URL.
|
||||
*/
|
||||
private void assertBytestreamRedirectInfo(String message, String aliasUrl) {
|
||||
assertEquals(message + ": expecting BYTESTREAM_REDIRECT request type",
|
||||
IndividualRequestInfo.Type.BYTESTREAM_REDIRECT,
|
||||
requestInfo.getType());
|
||||
assertEquals(message + ": expected alias URL", aliasUrl,
|
||||
requestInfo.getRedirectUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* We should have a NO_INDIVIDUAL request.
|
||||
*/
|
||||
private void assertNoIndividualRequestInfo(String message) {
|
||||
assertEquals(message + ": expecting NO_INDIVIDUAL request type",
|
||||
IndividualRequestInfo.Type.NO_INDIVIDUAL, requestInfo.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* We should have a LINKED_DATA request, with the expected Individual and
|
||||
* content type.
|
||||
*/
|
||||
private void assertLinkedDataRequestInfo(String message,
|
||||
String individualUri, ContentType contentType) {
|
||||
assertEquals(message + ": expecting LINKED_DATA request type",
|
||||
IndividualRequestInfo.Type.LINKED_DATA, requestInfo.getType());
|
||||
assertNotNull(message + ": expected an individual",
|
||||
requestInfo.getIndividual());
|
||||
assertEquals(message + ": expected individual", individualUri,
|
||||
requestInfo.getIndividual().getURI());
|
||||
assertNotNull(message + ": expected a content type",
|
||||
requestInfo.getRdfFormat());
|
||||
assertEquals(message + ": expected contentType", contentType,
|
||||
requestInfo.getRdfFormat());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Supporting classes
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
private static class MyAnalysisContext implements
|
||||
IndividualRequestAnalysisContext {
|
||||
// ----------------------------------------------------------------------
|
||||
// Stub infrastructure
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
private final String defaultNamespace;
|
||||
private final Map<String, Individual> individualsByUri = new HashMap<String, Individual>();
|
||||
private final Map<String, Individual> profilePages = new HashMap<String, Individual>();
|
||||
private final Map<String, String> namespacesByPrefix = new HashMap<String, String>();
|
||||
private final Map<String, String> aliasUrlsByIndividual = new HashMap<String, String>();
|
||||
|
||||
public MyAnalysisContext(String defaultNamespace) {
|
||||
this.defaultNamespace = defaultNamespace;
|
||||
}
|
||||
|
||||
public void addIndividual(Individual individual) {
|
||||
individualsByUri.put(individual.getURI(), individual);
|
||||
}
|
||||
|
||||
public void addProfilePage(String netId, Individual individual) {
|
||||
profilePages.put(netId, individual);
|
||||
}
|
||||
|
||||
public void setNamespacePrefix(String prefix, String namespace) {
|
||||
namespacesByPrefix.put(prefix, namespace);
|
||||
}
|
||||
|
||||
public void setAliasUrl(String individualUri, String aliasUrl) {
|
||||
aliasUrlsByIndividual.put(individualUri, aliasUrl);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Stub methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public String getDefaultNamespace() {
|
||||
return defaultNamespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespaceForPrefix(String prefix) {
|
||||
if (prefix == null) {
|
||||
return "";
|
||||
}
|
||||
String namespace = namespacesByPrefix.get(prefix);
|
||||
return (namespace == null) ? "" : namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Individual getIndividualByURI(String individualUri) {
|
||||
if (individualUri == null) {
|
||||
return null;
|
||||
|
||||
}
|
||||
return individualsByUri.get(individualUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Individual getIndividualByNetId(String netId) {
|
||||
if (netId == null) {
|
||||
return null;
|
||||
}
|
||||
return profilePages.get(netId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAliasUrlForBytestreamIndividual(Individual individual) {
|
||||
if (individual == null) {
|
||||
return null;
|
||||
}
|
||||
return aliasUrlsByIndividual.get(individual.getURI());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import junit.framework.Assert;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.IndividualController;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.individual.IndividualController;
|
||||
|
||||
|
||||
public class ContentTypeTest {
|
||||
|
@ -41,7 +41,7 @@ public class ContentTypeTest {
|
|||
Map<String,Float> clientAccepts = ContentType.getTypesAndQ(
|
||||
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/rdf+xml;q=0.93,text/rdf+n3;q=0.5");
|
||||
|
||||
Map<String,Float> serverTypes = IndividualController.getAcceptedContentTypes();
|
||||
Map<String,Float> serverTypes = IndividualController.ACCEPTED_CONTENT_TYPES;
|
||||
|
||||
Assert.assertEquals("application/rdf+xml", ContentType.getBestContentType(clientAccepts, serverTypes));
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ public class ContentTypeTest {
|
|||
Map<String,Float> clientAccepts = ContentType.getTypesAndQ(
|
||||
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
|
||||
|
||||
Map<String,Float> serverTypes = IndividualController.getAcceptedContentTypes();
|
||||
Map<String,Float> serverTypes = IndividualController.ACCEPTED_CONTENT_TYPES;
|
||||
|
||||
Assert.assertEquals("application/xhtml+xml", ContentType.getBestContentType(clientAccepts, serverTypes));
|
||||
}
|
||||
|
|
|
@ -986,7 +986,7 @@
|
|||
|
||||
<servlet>
|
||||
<servlet-name>individual</servlet-name>
|
||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.freemarker.IndividualController</servlet-class>
|
||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.individual.IndividualController</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
<#-- Template for help on individual page -->
|
||||
|
||||
<h2>Quick Notes on Using Individual:</h2>
|
||||
<h2>Individual not found:</h2>
|
||||
|
||||
<p>id is the id of the entity to query for. netid also works.</p>
|
Loading…
Add table
Add a link
Reference in a new issue