1 package com.atlassian.cache.servlet;
2
3 import com.atlassian.cache.servlet.handler.CacheExpirationHandler;
4 import com.atlassian.cache.servlet.handler.LastModifiedCacheExpirationHandler;
5 import com.atlassian.cache.servlet.resolver.ContentResolver;
6 import org.apache.commons.collections.MultiHashMap;
7 import org.apache.commons.collections.MultiMap;
8 import org.apache.log4j.Logger;
9
10 import javax.servlet.ServletConfig;
11 import javax.servlet.ServletException;
12 import javax.servlet.http.HttpServlet;
13 import javax.servlet.http.HttpServletRequest;
14 import javax.servlet.http.HttpServletResponse;
15 import java.io.IOException;
16 import java.util.Collection;
17 import java.util.Enumeration;
18 import java.util.HashMap;
19 import java.util.Iterator;
20 import java.util.Set;
21 import java.util.TreeSet;
22
23 public class CombinedCachingServlet extends HttpServlet
24 {
25 protected final Logger log = Logger.getLogger(this.getClass());
26
27 private static final String DEFAULT_CONTENT_TYPE = "text/javascript";
28 private static final String CONTENT_RESOLVER_PREFIX = "content.resolver";
29
30 public static final String DEFAULT_ENCODING = "UTF-8";
31
32 private String data = null;
33 private CacheExpirationHandler cacheExpirationHandler;
34 private String contentType;
35 private HashMap contentResolvers;
36 private MultiMap contentByResolver;
37
38 public synchronized void init(ServletConfig servletConfig) throws ServletException
39 {
40 super.init(servletConfig);
41
42 initializeCacheExpirationHandler(servletConfig.getInitParameter("cache.expiration.handler.class"));
43 initializeContentType(servletConfig.getInitParameter("content.type"));
44 initializeContentResolvers(servletConfig);
45 initializeContentParams();
46 }
47
48 protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
49 {
50
51 response.setContentType(contentType);
52
53
54 if (getCacheExpirationHandler().checkRequest(request, response))
55 {
56 return;
57 }
58
59 response.getWriter().write(getDataToSend(request, response));
60 response.getWriter().flush();
61 }
62
63 private String getDataToSend(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
64 {
65 if (isCacheDisabled())
66 return makeData(request, response);
67 else
68 return getCachedData(request, response);
69 }
70
71 private synchronized String getCachedData(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
72 {
73 if (data == null)
74 data = makeData(request, response);
75
76 return data;
77 }
78
79 private boolean isCacheDisabled()
80 {
81 return "true".equals(System.getProperty("atlassian.disable.caches", "false"));
82 }
83
84 private String makeData(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
85 {
86 try
87 {
88 StringBuffer buffer = new StringBuffer();
89
90
91 for (Iterator iterator = contentResolvers.keySet().iterator(); iterator.hasNext();)
92 {
93 String resolverKey = (String) iterator.next();
94
95 if (contentByResolver.containsKey(resolverKey))
96 {
97
98 for (Iterator iterator1 = ((Collection) contentByResolver.get(resolverKey)).iterator(); iterator1.hasNext();)
99 {
100 String path = (String) iterator1.next();
101 ContentResolver resolver = (ContentResolver) contentResolvers.get(resolverKey);
102 buffer.append(resolver.getContent(path, request, response));
103 }
104 }
105 }
106 return buffer.toString();
107 }
108 catch (IOException e)
109 {
110 log.error("Error building combined cached servlet data: " + request.getRequestURI() + " - " + e.toString(), e);
111 throw e;
112 }
113 catch (ServletException e)
114 {
115 log.error("Error building combined cached servlet data: " + request.getRequestURI() + " - " + e.toString(), e);
116 throw e;
117 }
118 }
119
120 private CacheExpirationHandler getCacheExpirationHandler()
121 {
122 return cacheExpirationHandler;
123 }
124
125
126
127
128
129 private void initializeContentParams()
130 {
131 contentByResolver = new MultiHashMap();
132
133 Enumeration e = getInitParameterNames();
134 Set sortedNames = new TreeSet();
135 while (e.hasMoreElements())
136 {
137 sortedNames.add(e.nextElement());
138 }
139
140 for (Iterator iterator = contentResolvers.keySet().iterator(); iterator.hasNext();)
141 {
142 String resolverKey = (String) iterator.next();
143
144 for (Iterator i = sortedNames.iterator(); i.hasNext();)
145 {
146 String name = (String) i.next();
147 if (name.startsWith(resolverKey))
148 {
149
150 contentByResolver.put(resolverKey, getInitParameter(name));
151 }
152 }
153
154 }
155 }
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 private void initializeContentResolvers(ServletConfig config) throws ServletException
174 {
175 contentResolvers = new HashMap();
176
177 for (Enumeration e = getInitParameterNames(); e.hasMoreElements();)
178 {
179 String name = (String) e.nextElement();
180 if (name.startsWith(CONTENT_RESOLVER_PREFIX))
181 {
182 Object objResolver = getObjectForClass(getInitParameter(name));
183 if (objResolver != null && objResolver instanceof ContentResolver)
184 {
185 ContentResolver resolver = (ContentResolver) objResolver;
186
187 resolver.init(config);
188
189
190 String key = name.substring(CONTENT_RESOLVER_PREFIX.length() + 1);
191 contentResolvers.put(key, resolver);
192 }
193 else
194 {
195 log.warn("The class " + getInitParameter(name) + " does not implement ContentResolver.");
196 }
197 }
198 }
199 }
200
201 private void initializeContentType(String initParameter)
202 {
203 if (initParameter == null)
204 {
205 contentType = DEFAULT_CONTENT_TYPE;
206 }
207 else
208 {
209 contentType = initParameter;
210 }
211 }
212
213 private void initializeCacheExpirationHandler(String cacheExpirationHandlerClass)
214 {
215 Object objCacheExpirationHandler = getObjectForClass(cacheExpirationHandlerClass);
216
217 if (objCacheExpirationHandler != null && objCacheExpirationHandler instanceof CacheExpirationHandler)
218 {
219 cacheExpirationHandler = (CacheExpirationHandler) objCacheExpirationHandler;
220 }
221 else
222 {
223 log.info("Unable to resolve class: " + cacheExpirationHandlerClass + " as an implementation of CacheExpirationHandler");
224
225 cacheExpirationHandler = new LastModifiedCacheExpirationHandler();
226 log.info("Using the LastModifiedCacheExpirationHandler cache expiration handler");
227 }
228 }
229
230 private Object getObjectForClass(String clazz)
231 {
232 Object obj = null;
233 try
234 {
235 if (clazz != null)
236 {
237 obj = Class.forName(clazz).newInstance();
238 }
239 }
240 catch (Exception e)
241 {
242 log.warn("Unable to resolve class for className: " + clazz);
243 }
244 return obj;
245 }
246 }