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.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; 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.beans.ApplicationBean;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.config.RevisionInfoBean; 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.controller.freemarker.UrlBuilder.Route;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.configuration.EditConfigurationConstants; 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.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.directives.IndividualShortViewDirective;
import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualLocalNameMethod; import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualLocalNameMethod;
import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualPlaceholderImageUrlMethod; import edu.cornell.mannlib.vitro.webapp.web.methods.IndividualPlaceholderImageUrlMethod;
@ -28,11 +36,15 @@ import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader; import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader; import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader; import freemarker.cache.TemplateLoader;
import freemarker.core.Environment;
import freemarker.ext.beans.BeansWrapper; import freemarker.ext.beans.BeansWrapper;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper; import freemarker.template.DefaultObjectWrapper;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
import freemarker.template.TemplateModelException; import freemarker.template.TemplateModelException;
import freemarker.template.utility.DeepUnwrap;
public class FreemarkerConfiguration extends Configuration { public class FreemarkerConfiguration extends Configuration {
@ -203,4 +215,101 @@ public class FreemarkerConfiguration extends Configuration {
return mtl; 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")); 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(); env.process();
} catch (TemplateException e) { } catch (TemplateException e) {
throw new TemplateProcessingException("TemplateException creating processing environment", 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 * This should not return PageDataGetters and should not throw an
* exception if a page has PageDataGetters. * exception if a page has PageDataGetters.
*/ */
public static List<DataGetter> getDataGettersForPage( VitroRequest vreq, Model displayModel, String pageURI) public static List<DataGetter> getDataGettersForPage(VitroRequest vreq, Model displayModel, String pageURI)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{ throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException {
//get data getter uris for pageURI List<String> dgUris = getDataGetterURIsForAssociatedURI(displayModel, pageURI);
List<String> dgUris = getDataGetterURIsForPageURI( displayModel, pageURI); List<DataGetter> dgList = dataGettersForURIs(vreq, displayModel, dgUris);
log.debug("getDataGettersForPage: " + dgList);
List<DataGetter> dgList = new ArrayList<DataGetter>(); return dgList;
for( String dgURI: dgUris){ }
DataGetter dg =dataGetterForURI(vreq, displayModel, dgURI) ;
if( dg != null )
dgList.add(dg);
}
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 . * 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) public static List<DataGetter> getDataGettersForClass( VitroRequest vreq, Model displayModel, String classURI)
throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{ throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{
//get data getter uris for pageURI List<String> dgUris = getDataGetterURIsForAssociatedURI( displayModel, classURI);
List<String> dgUris = getDataGetterURIsForClassURI( displayModel, classURI); List<DataGetter> dgList = dataGettersForURIs(vreq, displayModel, dgUris);
log.debug("getDataGettersForClass: " + dgList);
List<DataGetter> dgList = new ArrayList<DataGetter>(); return dgList;
for( String dgURI: dgUris){ }
DataGetter dg =dataGetterForURI(vreq, displayModel, dgURI) ;
if( dg != null ) /**
dgList.add(dg); * 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".
log.debug("getDataGettersForClass: " + dgList); */
return dgList; 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 * Returns a DataGetter using information in the
@ -103,7 +122,7 @@ public class DataGetterUtils {
* that does not implement the DataGetter interface. * that does not implement the DataGetter interface.
*/ */
public static DataGetter dataGetterForURI(VitroRequest vreq, Model displayModel, String dataGetterURI) 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 //get java class for dataGetterURI
String dgClassName = getJClassForDataGetterURI(displayModel, 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 + String query = prefixes +
"SELECT ?dataGetter WHERE { ?pageURI display:hasDataGetter ?dataGetter. }"; "SELECT ?dataGetter WHERE { ?associatedURI display:hasDataGetter ?dataGetter }";
Query dgForPageQuery = QueryFactory.create(query); Query dgForUriQuery = QueryFactory.create(query);
QuerySolutionMap initialBindings = new QuerySolutionMap(); QuerySolutionMap initialBindings = new QuerySolutionMap();
initialBindings.add("pageURI", ResourceFactory.createResource( pageURI )); initialBindings.add("associatedURI", ResourceFactory.createResource( associatedURI ));
List<String> dgURIs = new ArrayList<String>(); List<String> dgURIs = new ArrayList<String>();
displayModel.enterCriticalSection(false); displayModel.enterCriticalSection(false);
try{ try{
QueryExecution qexec = QueryExecutionFactory.create(dgForPageQuery,displayModel,initialBindings ); QueryExecution qexec = QueryExecutionFactory.create(dgForUriQuery,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 );
try{ try{
ResultSet results = qexec.execSelect(); ResultSet results = qexec.execSelect();
while (results.hasNext()) { while (results.hasNext()) {
@ -233,6 +223,7 @@ public class DataGetterUtils {
}finally{ qexec.close(); } }finally{ qexec.close(); }
}finally{ displayModel.leaveCriticalSection(); } }finally{ displayModel.leaveCriticalSection(); }
log.debug("Found " + dgURIs.size() +" DataGetter URIs for '" + associatedURI + "': " + dgURIs);
return dgURIs; return dgURIs;
} }