NIHVIVO-2411 Implement the BROWSE short view, which uses AJAX calls and so requires a FreemarkerProcessingService to render the view into HTML.

This commit is contained in:
j2blake 2012-05-01 16:27:07 +00:00
parent cf94490842
commit 47b3565e58
7 changed files with 322 additions and 10 deletions

View file

@ -0,0 +1,79 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller.json;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
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.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService;
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewService.ShortViewContext;
import edu.cornell.mannlib.vitro.webapp.services.shortview.ShortViewServiceSetup;
import edu.cornell.mannlib.vitro.webapp.web.templatemodels.individual.IndividualTemplateModel;
/**
* Does a Solr search for individuals, and uses the short view to render each of
* the results.
*/
public class GetRenderedSolrIndividualsByVClass extends JsonObjectProducer {
private static final Log log = LogFactory
.getLog(GetRenderedSolrIndividualsByVClass.class);
protected GetRenderedSolrIndividualsByVClass(VitroRequest vreq) {
super(vreq);
}
/**
* Search for individuals by VClass. The class URI and the paging
* information are in the request parameters.
*/
@Override
protected JSONObject process() throws Exception {
JSONObject rObj = null;
VClass vclass = getVclassParameter(vreq);
String vclassId = vclass.getURI();
vreq.setAttribute("displayType", vclassId);
rObj = JsonServlet.getSolrIndividualsByVClass(vclassId, vreq, ctx);
addShortViewRenderings(rObj);
return rObj;
}
/**
* Look through the return object. For each individual, render the short
* view and insert the resulting HTML into the object.
*/
private void addShortViewRenderings(JSONObject rObj) throws JSONException {
JSONArray individuals = rObj.getJSONArray("individuals");
String vclassName = rObj.getJSONObject("vclass").getString("name");
for (int i = 0; i < individuals.length(); i++) {
JSONObject individual = individuals.getJSONObject(i);
individual.put("shortViewHtml",
renderShortView(individual.getString("URI"), vclassName));
}
}
private String renderShortView(String individualUri, String vclassName) {
IndividualDao iDao = vreq.getWebappDaoFactory().getIndividualDao();
Individual individual = iDao.getIndividualByURI(individualUri);
Map<String, Object> modelMap = new HashMap<String, Object>();
modelMap.put("individual",
new IndividualTemplateModel(individual, vreq));
modelMap.put("vclass", vclassName);
ShortViewService svs = ShortViewServiceSetup.getService(ctx);
return svs.renderShortView(individual, ShortViewContext.BROWSE,
modelMap, vreq);
}
}

View file

@ -32,6 +32,7 @@ import edu.cornell.mannlib.vitro.webapp.utils.pageDataGetter.PageDataGetterUtils
* It could be generalized to get other types of data ex. XML, HTML etc * It could be generalized to get other types of data ex. XML, HTML etc
* @author bdc34 * @author bdc34
* *
* Moved most of the logic into a group of JsonProducer classes. jeb228
*/ */
public class JsonServlet extends VitroHttpServlet { public class JsonServlet extends VitroHttpServlet {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -69,8 +70,8 @@ public class JsonServlet extends VitroHttpServlet {
new GetSolrIndividualsByVClasses(vreq).process(resp); new GetSolrIndividualsByVClasses(vreq).process(resp);
} else if( vreq.getParameter("getDataForPage") != null ){ } else if( vreq.getParameter("getDataForPage") != null ){
new GetDataForPage(vreq).process(resp); new GetDataForPage(vreq).process(resp);
// }else if( vreq.getParameter("getRenderedSolrIndividualsByVClass") != null ){ }else if( vreq.getParameter("getRenderedSolrIndividualsByVClass") != null ){
// new GetRenderedSolrIndividualsByVClass(vreq).process(resp); new GetRenderedSolrIndividualsByVClass(vreq).process(resp);
} }
} }

View file

@ -0,0 +1,86 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.services.freemarker;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
/**
* Provide the ability to process a Freemarker template outside of the context
* of a FreemarkerHttpServlet.
*
* The most likely place to use this is when rendering a short view that was
* invoked through an AJAX call.
*/
public interface FreemarkerProcessingService {
/**
* Is there an accessible template by this name?
*
* The question is asked in the context of the current request, which
* determines the theme directory.
*
* @throws TemplateProcessingException
* If the template is found, but cannot be parsed.
*/
boolean isTemplateAvailable(String templateName, HttpServletRequest req)
throws TemplateProcessingException;
/**
* Process a Freemarker template with a data map, producing string of HTML.
*
* This is done in the context of the current HttpServletRequest, which
* provides a wide range of ancillary information, including (but not
* limited to) theme directory, context path, info on logged-in user,
* authorizations for the current user, etc., etc.
*/
String renderTemplate(String templateName, Map<String, Object> map,
HttpServletRequest req) throws TemplateProcessingException;
/**
* Indicates a failure to render the given template with the given data.
*/
@SuppressWarnings("serial")
public static class TemplateProcessingException extends Exception {
public TemplateProcessingException() {
super();
}
public TemplateProcessingException(String message) {
super(message);
}
public TemplateProcessingException(Throwable cause) {
super(cause);
}
public TemplateProcessingException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* Template parser detected a problem.
*/
@SuppressWarnings("serial")
public static class TemplateParsingException extends
TemplateProcessingException {
public TemplateParsingException() {
super();
}
public TemplateParsingException(String message) {
super(message);
}
public TemplateParsingException(Throwable cause) {
super(cause);
}
public TemplateParsingException(String message, Throwable cause) {
super(message, cause);
}
}
}

View file

@ -0,0 +1,99 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.services.freemarker;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
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.FreemarkerConfiguration;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerConfigurationLoader;
import edu.cornell.mannlib.vitro.webapp.utils.log.LogUtils;
import freemarker.core.Environment;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* An implementation of the FreemarkerProcessingService.
*/
public class FreemarkerProcessingServiceImpl implements
FreemarkerProcessingService {
private static final Log log = LogFactory
.getLog(FreemarkerProcessingServiceImpl.class);
@Override
public boolean isTemplateAvailable(String templateName,
HttpServletRequest req) throws TemplateProcessingException {
return null != getTemplate(templateName, req);
}
@Override
public String renderTemplate(String templateName, Map<String, Object> map,
HttpServletRequest req) throws TemplateProcessingException {
log.debug("renderTemplate: '" + templateName + "' with "
+ LogUtils.deepFormatForLog(log, "debug", map));
Template template = getTemplate(templateName, req);
return processTemplate(template, map, req);
}
/**
* Fetch this template from a file and parse it. If there are any problems,
* return "null".
*/
private Template getTemplate(String templateName, HttpServletRequest req)
throws TemplateProcessingException {
Template template = null;
try {
Configuration config = FreemarkerConfigurationLoader
.getConfig(new VitroRequest(req));
template = config.getTemplate(templateName);
} catch (ParseException e) {
log.warn("Failed to parse the template at '" + templateName + "'"
+ e);
throw new TemplateParsingException(e);
} catch (FileNotFoundException e) {
log.debug("No template found for '" + templateName + "'");
throw new TemplateProcessingException(e);
} catch (IOException e) {
log.warn("Failed to read the template at '" + templateName + "'", e);
throw new TemplateProcessingException(e);
}
return template;
}
private String processTemplate(Template template, Map<String, Object> map,
HttpServletRequest req) throws TemplateProcessingException {
StringWriter writer = new StringWriter();
try {
// Add directives to the map. For some reason, having them in the
// configuration is not enough.
map.putAll(FreemarkerConfiguration.getDirectives());
// Add request and servlet context as custom attributes of the
// environment, so they
// can be used in directives.
Environment env = template.createProcessingEnvironment(map, writer);
env.setCustomAttribute("request", req);
env.setCustomAttribute("context", req.getSession()
.getServletContext());
env.process();
return writer.toString();
} catch (TemplateException e) {
throw new TemplateProcessingException(
"TemplateException creating processing environment", e);
} catch (IOException e) {
throw new TemplateProcessingException(
"IOException creating processing environment", e);
}
}
}

View file

@ -0,0 +1,15 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.services.freemarker;
import javax.servlet.ServletContext;
/**
* This would be used to create and publish such a service, but for now we can
* simply create a fresh copy every time someone asks for one.
*/
public class FreemarkerProcessingServiceSetup {
public static FreemarkerProcessingService getService(ServletContext ctx) {
return new FreemarkerProcessingServiceImpl();
}
}

View file

@ -67,7 +67,8 @@ public interface ShortViewService {
* The available contexts for short views. * The available contexts for short views.
*/ */
public enum ShortViewContext { public enum ShortViewContext {
SEARCH("view-search-default.ftl"), INDEX("listedIndividual.ftl"); SEARCH("view-search-default.ftl"), INDEX("listedIndividual.ftl"), BROWSE(
"view-browse-default.ftl");
private final String defaultTemplateName; private final String defaultTemplateName;

View file

@ -13,10 +13,16 @@ import java.util.TreeSet;
import javax.servlet.ServletContext; 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.Individual;
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.services.freemarker.FreemarkerProcessingService;
import edu.cornell.mannlib.vitro.webapp.services.freemarker.FreemarkerProcessingService.TemplateParsingException;
import edu.cornell.mannlib.vitro.webapp.services.freemarker.FreemarkerProcessingServiceSetup;
import edu.cornell.mannlib.vitro.webapp.services.shortview.FakeApplicationOntologyService.TemplateAndDataGetters; import edu.cornell.mannlib.vitro.webapp.services.shortview.FakeApplicationOntologyService.TemplateAndDataGetters;
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter; import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
@ -24,6 +30,9 @@ import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
* The basic implementation of ShortViewService * The basic implementation of ShortViewService
*/ */
public class ShortViewServiceImpl implements ShortViewService { public class ShortViewServiceImpl implements ShortViewService {
private static final Log log = LogFactory
.getLog(ShortViewServiceImpl.class);
private static final Map<String, Object> EMPTY_MAP = Collections.emptyMap(); private static final Map<String, Object> EMPTY_MAP = Collections.emptyMap();
private final FakeApplicationOntologyService faker; private final FakeApplicationOntologyService faker;
@ -39,17 +48,38 @@ public class ShortViewServiceImpl implements ShortViewService {
TemplateAndSupplementalData tsd = getShortViewInfo(individual, context, TemplateAndSupplementalData tsd = getShortViewInfo(individual, context,
vreq); vreq);
String templateName = tsd.getTemplateName();
Map<String, Object> supplementalData = tsd.getSupplementalData();
// TODO Auto-generated method stub try {
throw new RuntimeException( Map<String, Object> fullModelMap = new HashMap<String, Object>(
"ShortViewService.renderShortView() not implemented."); modelMap);
fullModelMap.putAll(supplementalData);
FreemarkerProcessingService fps = FreemarkerProcessingServiceSetup
.getService(vreq.getSession().getServletContext());
if (!fps.isTemplateAvailable(templateName, vreq)) {
return "<p>Can't locate short view template '" + templateName
+ "' for " + individual.getName() + "</p>";
}
return fps.renderTemplate(templateName, fullModelMap, vreq);
} catch (TemplateParsingException e) {
return "<p>Can't parse the short view template '" + templateName
+ "' for " + individual.getName() + "</p>";
} catch (Exception e) {
log.error(e, e);
return "<p>Failed to render the short view for "
+ individual.getName() + "</p>";
}
} }
@Override @Override
public TemplateAndSupplementalData getShortViewInfo(Individual individual, public TemplateAndSupplementalData getShortViewInfo(Individual individual,
ShortViewContext svContext, VitroRequest vreq) { ShortViewContext svContext, VitroRequest vreq) {
TemplateAndDataGetters tdg = fetchTemplateAndDataGetters(individual, TemplateAndDataGetters tdg = fetchTemplateAndDataGetters(individual,
svContext); svContext, vreq);
Map<String, Object> gotData = runDataGetters(tdg.getDataGetters(), vreq); Map<String, Object> gotData = runDataGetters(tdg.getDataGetters(), vreq);
return new TemplateAndSupplementalDataImpl(tdg.getTemplateName(), return new TemplateAndSupplementalDataImpl(tdg.getTemplateName(),
gotData); gotData);
@ -68,18 +98,19 @@ public class ShortViewServiceImpl implements ShortViewService {
/** Find the template and data getters for this individual in this context. */ /** Find the template and data getters for this individual in this context. */
private TemplateAndDataGetters fetchTemplateAndDataGetters( private TemplateAndDataGetters fetchTemplateAndDataGetters(
Individual individual, ShortViewContext svContext) { Individual individual, ShortViewContext svContext, VitroRequest vreq) {
List<String> classUris = new ArrayList<String>(); List<String> classUris = new ArrayList<String>();
classUris.addAll(figureMostSpecificClassUris(individual)); classUris.addAll(figureMostSpecificClassUris(individual));
for (String classUri : classUris) { for (String classUri : classUris) {
TemplateAndDataGetters tdg = faker.getShortViewProperties(classUri, TemplateAndDataGetters tdg = faker.getShortViewProperties(
vreq.getWebappDaoFactory(), individual, classUri,
svContext.name()); svContext.name());
if (tdg != null) { if (tdg != null) {
return tdg; return tdg;
} }
} }
// Didn't find one? Use the default values. // Didn't find one? Use the default values.
return new TemplateAndDataGetters(svContext.getDefaultTemplateName()); return new TemplateAndDataGetters(svContext.getDefaultTemplateName());
} }