VIVO-10 Add the ability to associate a DataGetter with a Freemarker template.

This commit is contained in:
j2blake 2013-01-15 12:59:58 -05:00
parent caf16c392b
commit 6901a67670
3 changed files with 167 additions and 63 deletions

View file

@ -5,11 +5,16 @@ package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -17,9 +22,12 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vitro.webapp.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.config.RevisionInfoBean;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.Route;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.EditConfigurationConstants;
import edu.cornell.mannlib.vitro.webapp.i18n.freemarker.I18nMethodModel;
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetterUtils;
import edu.cornell.mannlib.vitro.webapp.web.directives.IndividualShortViewDirective;
import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualLocalNameMethod;
import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualPlaceholderImageUrlMethod;
@ -28,11 +36,15 @@ import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.core.Environment;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModelException;
import freemarker.template.utility.DeepUnwrap;
public class FreemarkerConfiguration extends Configuration {
@ -203,4 +215,101 @@ public class FreemarkerConfiguration extends Configuration {
return mtl;
}
/**
* Override getTemplate(), so we can apply DataGetters to all included
* templates.
*
* This won't work for top-level Templates, since the Environment hasn't
* been created yet. When TemplateProcessingHelper creates the Environment,
* it must call retrieveAndRunDataGetters() for the top-level Template.
*/
@Override
public Template getTemplate(String name, Locale locale, String encoding,
boolean parse) throws IOException {
Template template = super.getTemplate(name, locale, encoding, parse);
if (template == null) {
log.debug("Template '" + name + "' not found for locale '" + locale + "'.");
return template;
}
Environment env = getEnvironment();
if (env == null) {
log.debug("Not fetching data getters for template '" + template.getName() + "'. No environment.");
return template;
}
retrieveAndRunDataGetters(env, template.getName());
return template;
}
/**
* Find the DataGetters for this template, and apply them to the Freemarker
* environment.
*/
public static void retrieveAndRunDataGetters(Environment env, String templateName) {
HttpServletRequest req = (HttpServletRequest) env.getCustomAttribute("request");
VitroRequest vreq = new VitroRequest(req);
if (dataGettersAlreadyApplied(env, templateName)) {
log.debug("DataGetters for '" + templateName+"' have already been applied");
return;
}
try {
List<DataGetter> dgList = DataGetterUtils.getDataGettersForTemplate(
vreq, vreq.getDisplayModel(), templateName);
log.debug("Retrieved " + dgList.size() + " data getters for template '" + templateName + "'");
@SuppressWarnings("unchecked")
Map<String, Object> dataMap = (Map<String, Object>) DeepUnwrap.permissiveUnwrap(env.getDataModel());
for (DataGetter dg : dgList) {
applyDataGetter(dg, env, dataMap);
}
} catch (Exception e) {
log.warn(e, e);
}
}
/**
* Have the DataGetters for this template already been applied to this environment?
* If not, record that they are being applied now.
*/
@SuppressWarnings("unchecked")
private static boolean dataGettersAlreadyApplied(Environment env, String templateName) {
Set<String> names;
Object o = env.getCustomAttribute("dataGettersApplied");
if (o instanceof Set) {
names = (Set<String>) o;
} else {
names = new HashSet<String>();
}
boolean added = names.add(templateName);
if (added) {
env.setCustomAttribute("dataGettersApplied", names);
return false;
} else {
return true;
}
}
/**
* Get the data from a DataGetter, and store it in global variables in the
* Freemarker environment.
*/
private static void applyDataGetter(DataGetter dg, Environment env,
Map<String, Object> dataMap) throws TemplateModelException {
Map<String, Object> moreData = dg.getData(dataMap);
ObjectWrapper wrapper = env.getObjectWrapper();
if (moreData != null) {
for (String key : moreData.keySet()) {
Object value = moreData.get(key);
env.setGlobalVariable(key, wrapper.wrap(value));
log.debug("Stored in environment: '" + key + "' = '" + value + "'");
}
}
}
}

View file

@ -61,6 +61,10 @@ public class TemplateProcessingHelper {
env.include(getTemplate("pageSetup.ftl"));
}
// Apply any data-getters that are associated with this template.
FreemarkerConfiguration.retrieveAndRunDataGetters(env, template.getName());
// Now process it.
env.process();
} catch (TemplateException e) {
throw new TemplateProcessingException("TemplateException creating processing environment", e);

View file

@ -59,39 +59,58 @@ public class DataGetterUtils {
* This should not return PageDataGetters and should not throw an
* exception if a page has PageDataGetters.
*/
public static List<DataGetter> getDataGettersForPage( VitroRequest vreq, Model displayModel, String pageURI)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{
//get data getter uris for pageURI
List<String> dgUris = getDataGetterURIsForPageURI( displayModel, pageURI);
List<DataGetter> dgList = new ArrayList<DataGetter>();
for( String dgURI: dgUris){
DataGetter dg =dataGetterForURI(vreq, displayModel, dgURI) ;
if( dg != null )
dgList.add(dg);
}
log.debug("getDataGettersForPage: " + dgList);
return dgList;
}
public static List<DataGetter> getDataGettersForPage(VitroRequest vreq, Model displayModel, String pageURI)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException {
List<String> dgUris = getDataGetterURIsForAssociatedURI(displayModel, pageURI);
List<DataGetter> dgList = dataGettersForURIs(vreq, displayModel, dgUris);
log.debug("getDataGettersForPage: " + dgList);
return dgList;
}
/**
* Get a list of DataGetter objects that are associated with a JAVA class.
* Get a list of DataGetter objects that are associated with a Vitro VClass.
* This allows the individual profile for an individual of a specific class to be returned .
*/
public static List<DataGetter> getDataGettersForClass( VitroRequest vreq, Model displayModel, String classURI)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{
//get data getter uris for pageURI
List<String> dgUris = getDataGetterURIsForClassURI( displayModel, classURI);
List<DataGetter> dgList = new ArrayList<DataGetter>();
for( String dgURI: dgUris){
DataGetter dg =dataGetterForURI(vreq, displayModel, dgURI) ;
if( dg != null )
dgList.add(dg);
}
log.debug("getDataGettersForClass: " + dgList);
return dgList;
}
throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{
List<String> dgUris = getDataGetterURIsForAssociatedURI( displayModel, classURI);
List<DataGetter> dgList = dataGettersForURIs(vreq, displayModel, dgUris);
log.debug("getDataGettersForClass: " + dgList);
return dgList;
}
/**
* Get a list of DataGetter objects that are associated with a Freemarker template.
* @param templateName a filename like "index.ftl", which will be used as a URI like "freemarker:index.ftl".
*/
public static List<DataGetter> getDataGettersForTemplate( VitroRequest vreq, Model displayModel, String templateName)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{
String templateUri = "freemarker:" + templateName;
List<String> dgUris = getDataGetterURIsForAssociatedURI( displayModel, templateUri);
List<DataGetter> dgList = dataGettersForURIs(vreq, displayModel, dgUris);
log.debug("getDataGettersForTemplate '" + templateName + "': " + dgList);
return dgList;
}
/**
* Return a list of DataGetters from the list of URIs. Each DataGetter will be configured from information
* in the displayModel.
*
* Problems instantiating and configuring a particular DataGetter may result in an exception,
* or may just mean that there will be no entry in the result for that URI.
*
* May return an empty list, but will not return null.
*/
private static List<DataGetter> dataGettersForURIs(VitroRequest vreq, Model displayModel, List<String> dgUris)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException {
List<DataGetter> dgList = new ArrayList<DataGetter>();
for( String dgURI: dgUris){
DataGetter dg =dataGetterForURI(vreq, displayModel, dgURI) ;
if( dg != null )
dgList.add(dg);
}
return dgList;
}
/**
* Returns a DataGetter using information in the
@ -103,7 +122,7 @@ public class DataGetterUtils {
* that does not implement the DataGetter interface.
*/
public static DataGetter dataGetterForURI(VitroRequest vreq, Model displayModel, String dataGetterURI)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchMethodException
throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException, SecurityException
{
//get java class for dataGetterURI
String dgClassName = getJClassForDataGetterURI(displayModel, dataGetterURI);
@ -180,47 +199,18 @@ public class DataGetterUtils {
}
private static List<String> getDataGetterURIsForPageURI(Model displayModel, String pageURI) {
private static List<String> getDataGetterURIsForAssociatedURI(Model displayModel, String associatedURI) {
String query = prefixes +
"SELECT ?dataGetter WHERE { ?pageURI display:hasDataGetter ?dataGetter. }";
Query dgForPageQuery = QueryFactory.create(query);
"SELECT ?dataGetter WHERE { ?associatedURI display:hasDataGetter ?dataGetter }";
Query dgForUriQuery = QueryFactory.create(query);
QuerySolutionMap initialBindings = new QuerySolutionMap();
initialBindings.add("pageURI", ResourceFactory.createResource( pageURI ));
initialBindings.add("associatedURI", ResourceFactory.createResource( associatedURI ));
List<String> dgURIs = new ArrayList<String>();
displayModel.enterCriticalSection(false);
try{
QueryExecution qexec = QueryExecutionFactory.create(dgForPageQuery,displayModel,initialBindings );
try{
ResultSet results = qexec.execSelect();
while (results.hasNext()) {
QuerySolution soln = results.nextSolution();
Resource dg = soln.getResource("dataGetter");
if( dg != null && dg.getURI() != null){
dgURIs.add( dg.getURI());
}
}
}finally{ qexec.close(); }
}finally{ displayModel.leaveCriticalSection(); }
return dgURIs;
}
//Get data getters for a specific JAVA class - associates data getters with individuals for a specific JAVA class
private static List<String> getDataGetterURIsForClassURI(Model displayModel, String classURI) {
//Class URI will be substituted in so this is for a specific class uri
String query = prefixes +
"SELECT ?dataGetter WHERE { ?classURI display:hasDataGetter ?dataGetter }";
Query dgForPageQuery = QueryFactory.create(query);
QuerySolutionMap initialBindings = new QuerySolutionMap();
initialBindings.add("classURI", ResourceFactory.createResource( classURI ));
List<String> dgURIs = new ArrayList<String>();
displayModel.enterCriticalSection(false);
try{
QueryExecution qexec = QueryExecutionFactory.create(dgForPageQuery,displayModel,initialBindings );
QueryExecution qexec = QueryExecutionFactory.create(dgForUriQuery,displayModel,initialBindings );
try{
ResultSet results = qexec.execSelect();
while (results.hasNext()) {
@ -233,6 +223,7 @@ public class DataGetterUtils {
}finally{ qexec.close(); }
}finally{ displayModel.leaveCriticalSection(); }
log.debug("Found " + dgURIs.size() +" DataGetter URIs for '" + associatedURI + "': " + dgURIs);
return dgURIs;
}