View Javadoc
1   package com.atlassian.refapp.decorator;
2   
3   import com.atlassian.refapp.auth.external.WebSudoSessionManager;
4   import com.atlassian.soy.renderer.SoyException;
5   import com.atlassian.soy.renderer.SoyTemplateRenderer;
6   import com.atlassian.templaterenderer.RenderingException;
7   import com.atlassian.templaterenderer.TemplateRenderer;
8   import com.google.common.collect.ImmutableMap;
9   import com.opensymphony.module.sitemesh.HTMLPage;
10  import com.opensymphony.module.sitemesh.Page;
11  import com.opensymphony.module.sitemesh.RequestConstants;
12  import com.opensymphony.module.sitemesh.util.OutputConverter;
13  import org.apache.commons.io.IOUtils;
14  import org.slf4j.Logger;
15  import org.slf4j.LoggerFactory;
16  
17  import javax.servlet.ServletContext;
18  import javax.servlet.ServletException;
19  import javax.servlet.http.HttpServlet;
20  import javax.servlet.http.HttpServletRequest;
21  import javax.servlet.http.HttpServletResponse;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.PrintWriter;
25  import java.io.StringWriter;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.Map;
29  import java.util.Properties;
30  
31  public class DecoratorServlet extends HttpServlet {
32      private static final Logger log = LoggerFactory.getLogger(DecoratorServlet.class);
33      private static final String SERVLET_PATH = "/refappdecorator";
34      private static final String PROPERTIES_LOCATION = "/META-INF/maven/com.atlassian.refapp/atlassian-refapp/pom.properties";
35  
36      private final TemplateRenderer templateRenderer;
37      private final SoyTemplateRenderer soyTemplateRenderer;
38      private final WebSudoSessionManager webSudoSessionManager;
39  
40      public DecoratorServlet(TemplateRenderer templateRenderer, WebSudoSessionManager webSudoSessionManager, final SoyTemplateRenderer soyTemplateRenderer) {
41          this.templateRenderer = templateRenderer;
42          this.webSudoSessionManager = webSudoSessionManager;
43          this.soyTemplateRenderer = soyTemplateRenderer;
44      }
45  
46      protected void service(HttpServletRequest request, HttpServletResponse response)
47              throws ServletException, IOException {
48          Page page = (Page) request.getAttribute(RequestConstants.PAGE);
49          if (page != null) {
50              applyDecoratorUsingVelocity(request, page, response);
51          } else {
52              String servletPath = (String) request.getAttribute("javax.servlet.include.servlet_path");
53              if (servletPath == null) {
54                  servletPath = request.getServletPath();
55              }
56              throw new ServletException("No sitemesh page to decorate. This servlet should not be invoked directly. " +
57                      "The path invoked was " + servletPath);
58          }
59      }
60  
61      private void applyDecoratorUsingVelocity(HttpServletRequest request, Page page, HttpServletResponse response) throws
62              IOException {
63          String template;
64          String pathInfo = (String) request.getAttribute("javax.servlet.include.path_info");
65          if (pathInfo == null) {
66              pathInfo = request.getPathInfo();
67          }
68          if (pathInfo != null) {
69              template = mapPathToTemplate(pathInfo);
70          } else {
71              template = "/templates/general.vmd";
72          }
73  
74          Map<String, Object> velocityParams = getVelocityParams(request, page, response);
75  
76          final PrintWriter writer = response.getWriter();
77          try {
78              response.setContentType("text/html");
79              templateRenderer.render(template, velocityParams, writer);
80          } catch (RenderingException e) {
81              writer.write("Exception rendering velocity file " + template);
82              writer.write("<br><pre>");
83              e.printStackTrace(writer);
84              writer.write("</pre>");
85          }
86      }
87  
88      private String mapPathToTemplate(String pathInfo) {
89          if (pathInfo.startsWith(SERVLET_PATH)) {
90              return pathInfo.replace(SERVLET_PATH, "templates");
91          }
92  
93          return pathInfo;
94      }
95  
96      private Properties getPropertiesFromServletContext(String location) throws IOException {
97          ServletContext servletContext = getServletConfig().getServletContext();
98          InputStream in = servletContext.getResourceAsStream(location);
99  
100         if (in == null) {
101             throw new IOException("Resource '" + location + "' not found");
102         }
103 
104         try {
105             Properties properties = new Properties();
106             properties.load(in);
107             return properties;
108         } finally {
109             IOUtils.closeQuietly(in);
110         }
111     }
112 
113     private Map<String, Object> getVelocityParams(HttpServletRequest request, Page page, HttpServletResponse response)
114             throws IOException {
115         Map<String, Object> velocityParams = new HashMap<String, Object>();
116 
117         String version;
118         try {
119             Properties properties = getPropertiesFromServletContext(PROPERTIES_LOCATION);
120             version = properties.getProperty("version");
121         } catch (IOException e) {
122             version = "(unknown)";
123         }
124         velocityParams.put("version", version);
125 
126         velocityParams.put("page", page);
127 
128         if (webSudoSessionManager.isWebSudoSession(request)) {
129             velocityParams.put("websudosession", Boolean.TRUE.toString());
130         }
131 
132         String titleHtml = page.getTitle();
133         if (titleHtml == null || titleHtml.isEmpty()) {
134             titleHtml = "Welcome!";
135         }
136 
137         velocityParams.put("titleHtml", titleHtml);
138 
139         String htmlClass = page.getProperty("class");
140 
141         if (htmlClass != null) {
142             velocityParams.put("headClass", htmlClass);
143         }
144 
145         String bodyClass = page.getProperty("body.class");
146         if (bodyClass != null) {
147             velocityParams.put("bodyClass", bodyClass);
148         }
149 
150         StringWriter bodyBuffer = new StringWriter();
151         page.writeBody(OutputConverter.getWriter(bodyBuffer));
152         velocityParams.put("bodyHtml", bodyBuffer);
153 
154         if (page instanceof HTMLPage) {
155             HTMLPage htmlPage = (HTMLPage) page;
156             StringWriter buffer = new StringWriter();
157             htmlPage.writeHead(OutputConverter.getWriter(buffer));
158             velocityParams.put("headHtml", buffer.toString());
159         }
160 
161         try {
162             velocityParams.put("headerBeforeContent", soyTemplateRenderer.render("com.atlassian.plugins.atlassian-nav-links-plugin:rotp-menu",
163                     "navlinks.templates.appswitcher.switcher", Collections.<String, Object>emptyMap()));
164 
165 
166             // Build secondary navigation
167             StringWriter secondaryNavigation = new StringWriter();
168             templateRenderer.render("/templates/navigation.vm", new ImmutableMap.Builder<String, Object>()
169                             .put("request", request)
170                             .put("response", response)
171                             .build(),
172                     secondaryNavigation);
173 
174             velocityParams.put("secondaryNavContent", secondaryNavigation.toString());
175         } catch (SoyException e) {
176             log.error("Failed to render Soy template due to {}", e.getMessage(), e);
177         }
178 
179         velocityParams.put("request", request);
180         velocityParams.put("response", response);   // as of 2.5.0 both req and resp must be available in all apps
181         velocityParams.put("soy", soyTemplateRenderer);
182         return velocityParams;
183     }
184 }