View Javadoc

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          // always set the content type as defined in the init params
51          response.setContentType(contentType);
52  
53          // check to see if we need to do any work
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              // run through each contentResolver that is registered
91              for (Iterator iterator = contentResolvers.keySet().iterator(); iterator.hasNext();)
92              {
93                  String resolverKey = (String) iterator.next();
94                  // If we have content registered for this resolver
95                  if (contentByResolver.containsKey(resolverKey))
96                  {
97                      // Run through all the registered content, asking the resolver to resolve it.
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      * Run through all the registered content resolver keys and then run through the init params looking for
127      * content entries for each resolver key. Store the content path with the resolver key.
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         // Run through all the contentResolvers that were registered
140         for (Iterator iterator = contentResolvers.keySet().iterator(); iterator.hasNext();)
141         {
142             String resolverKey = (String) iterator.next();
143             // Run through all the init params looking for the resolver key as a prefix
144             for (Iterator i = sortedNames.iterator(); i.hasNext();)
145             {
146                 String name = (String) i.next();
147                 if (name.startsWith(resolverKey))
148                 {
149                     // store the path in the list associated with the resolver key
150                     contentByResolver.put(resolverKey, getInitParameter(name));
151                 }
152             }
153 
154         }
155     }
156 
157     /**
158      * Runs through all the init params looking for param names starting with CONTENT_RESOLVER_PREFIX. We will use
159      * the remainder of the name as the key (which is referenced in the content entries) which will be associated
160      * with the implementation of the content resolver (the class specified in the value of the param). If the class
161      * is not found then the resolver will not be added.
162      * <p/>
163      * ex.
164      * <pre>
165      *  &lt;init-param&gt;
166      *    &lt;param-name&gt;content.resolver.dwr&lt;/param-name&gt;
167      *    &lt;param-value&gt;com.atlassian.cache.servlet.resolver.DwrContentResolver&lt;/param-value&gt;
168      *  &lt;/init-param&gt;
169      * </pre>
170      * <p/>
171      * This will map the key: dwr to the implementation: com.atlassian.cache.servlet.resolver.DwrContentResolver
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                     // always make sure to initialize the resolver
187                     resolver.init(config);
188 
189                     // Get the key to map this resolver from the name and add the impl to the resolvers map
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             // fallback to the default implementation
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 }