diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java index cca16fc34..07a370779 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FreemarkerConfiguration.java @@ -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 dgList = DataGetterUtils.getDataGettersForTemplate( + vreq, vreq.getDisplayModel(), templateName); + log.debug("Retrieved " + dgList.size() + " data getters for template '" + templateName + "'"); + + @SuppressWarnings("unchecked") + Map dataMap = (Map) 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 names; + Object o = env.getCustomAttribute("dataGettersApplied"); + if (o instanceof Set) { + names = (Set) o; + } else { + names = new HashSet(); + } + + 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 dataMap) throws TemplateModelException { + Map 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 + "'"); + } + } + } + } diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java index 5d91a919e..c1fd229f3 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/TemplateProcessingHelper.java @@ -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); diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtils.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtils.java index 73e582dfe..bf9458a17 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtils.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/utils/dataGetter/DataGetterUtils.java @@ -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 getDataGettersForPage( VitroRequest vreq, Model displayModel, String pageURI) - throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{ - //get data getter uris for pageURI - List dgUris = getDataGetterURIsForPageURI( displayModel, pageURI); - - List dgList = new ArrayList(); - 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 getDataGettersForPage(VitroRequest vreq, Model displayModel, String pageURI) + throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException { + List dgUris = getDataGetterURIsForAssociatedURI(displayModel, pageURI); + List 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 getDataGettersForClass( VitroRequest vreq, Model displayModel, String classURI) - throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{ - //get data getter uris for pageURI - List dgUris = getDataGetterURIsForClassURI( displayModel, classURI); - - List dgList = new ArrayList(); - 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 dgUris = getDataGetterURIsForAssociatedURI( displayModel, classURI); + List 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 getDataGettersForTemplate( VitroRequest vreq, Model displayModel, String templateName) + throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{ + String templateUri = "freemarker:" + templateName; + List dgUris = getDataGetterURIsForAssociatedURI( displayModel, templateUri); + List 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 dataGettersForURIs(VitroRequest vreq, Model displayModel, List dgUris) + throws InstantiationException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException { + List dgList = new ArrayList(); + 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 getDataGetterURIsForPageURI(Model displayModel, String pageURI) { + private static List 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 dgURIs = new ArrayList(); 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 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 dgURIs = new ArrayList(); - 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; }