package flapjack.conf; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.inject.Inject; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.jersey.server.ContainerException; import org.glassfish.jersey.server.mvc.Viewable; import org.glassfish.jersey.server.mvc.spi.AbstractTemplateProcessor; import org.joda.time.DateTime; import org.jvnet.hk2.annotations.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import cambridge.ClassPathTemplateLoader; import cambridge.ExpressionLanguage; import cambridge.Template; import cambridge.TemplateFactory; import cambridge.TemplateLoader; import cambridge.jexl.JexlExpressionLanguage; import com.google.common.collect.ImmutableMap; import com.google.inject.Provider; import flapjack.entity.Person; import flapjack.entity.Session; import flapjack.exception.SessionTimeoutException; import flapjack.manager.PersonManager; import flapjack.utils.AppUtils; /** * A Jersey 2.x Template Processor for integrating with Cambridge Templates * * @author Ray Vanderborght */ @javax.ws.rs.ext.Provider public class CambridgeTemplateProcessor extends AbstractTemplateProcessor<Template> { @SuppressWarnings("unused") private static final Logger log = LoggerFactory.getLogger(CambridgeTemplateProcessor.class); /** Produces TemplateFactories */ private TemplateLoader loader; /** Cache these, they are basically the compiled version of a template */ private Map<String, TemplateFactory> factories = new ConcurrentHashMap<String, TemplateFactory>(); @Inject private Provider<PersonManager> personManagerProvider; @Inject private Provider<HttpServletRequest> requestProvider; /** * Create an instance of this processor with injected {@link javax.ws.rs.core.Configuration config} and * (optional) {@link javax.servlet.ServletContext servlet context}. * * @param config config to configure this processor from. * @param serviceLocator service locator to initialize template object factory if needed. * @param servletContext (optional) servlet context to obtain template resources from. */ @Inject public CambridgeTemplateProcessor(final javax.ws.rs.core.Configuration config, final ServiceLocator serviceLocator, @Optional final ServletContext servletContext) { super(config, servletContext, "cambridge", "html"); this.loader = new ClassPathTemplateLoader(getClass().getClassLoader()); } @Override protected Template resolve(final String templateReference, final Reader reader) throws Exception { TemplateFactory fact = null; if (isProd()) { fact = factories.get(templateReference); if (fact == null) { fact = loader.newTemplateFactory(templateReference, getExpressionLanguage()); factories.put(templateReference, fact); } } else { fact = loader.newTemplateFactory(templateReference, getExpressionLanguage()); } Template template = fact.createTemplate(); setCustomTemplateProperties(template); return template; } @Override public void writeTo(final Template template, final Viewable viewable, final MediaType mediaType, final MultivaluedMap<String, Object> httpHeaders, final OutputStream out) throws IOException { try { Object model = viewable.getModel(); if (!(model instanceof Map)) { model = ImmutableMap.of("model", viewable.getModel()); } Charset encoding = setContentType(mediaType, httpHeaders); Writer w = new OutputStreamWriter(out, encoding); template.setProperty("it", model); template.printTo(w); w.flush(); } catch (Exception e) { throw new ContainerException(e); } } /** * Configure the expression language to use in the templates */ protected ExpressionLanguage getExpressionLanguage() { JexlExpressionLanguage jexl = new JexlExpressionLanguage(); // jexl.getEngine().setSilent(false); // jexl.getEngine().setLenient(false); // Add a custom 'tool' binding with our TemplateTool class // http://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl2/package-summary.html#configuration Map<String, Object> funcs = new HashMap<String, Object>(); funcs.put("utils", AppUtils.class); jexl.getEngine().setFunctions(funcs); return jexl; } /** * Add any custom template properties here */ protected void setCustomTemplateProperties(Template template) { DateTime now = new DateTime(); PersonManager personManager = personManagerProvider.get(); HttpServletRequest request = requestProvider.get(); template.setProperty("app.name", "Flapjack"); template.setProperty("app.root", request.getContextPath()); template.setProperty("app.time", now); // find person's session and attach them if found Person person = null; try { Session session = personManager.getSession(request); if (session != null) { person = session.getPerson(); } template.setProperty("app.sessionTimeout", false); } catch (SessionTimeoutException e) { template.setProperty("app.sessionTimeout", true); } template.setProperty("user", new User(person)); } protected boolean isProd() { return false; } protected int checkInterval() { return isProd() ? -1 : 0; } /** * A collection of user related things for the frontend * * @author Ray Vanderborght */ public static class User { private Person person; public User(Person person) { this.person = person; } public boolean isLoggedIn() { return person != null; } public boolean accountRequiresSetup() { return isLoggedIn() ? person.getName() == null : false; } public Person getPerson() { return person; } } }