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
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);
181 velocityParams.put("soy", soyTemplateRenderer);
182 return velocityParams;
183 }
184 }