View Javadoc

1   package com.atlassian.plugin.webresource;
2   
3   import com.atlassian.plugin.cache.filecache.FileCacheStreamProvider;
4   import com.atlassian.plugin.servlet.DownloadException;
5   import com.atlassian.plugin.servlet.DownloadableResource;
6   import com.atlassian.plugin.webresource.cache.CacheHandle;
7   import com.google.common.base.Predicate;
8   import com.google.common.collect.ImmutableMap;
9   import com.google.common.collect.Iterables;
10  import org.slf4j.Logger;
11  import org.slf4j.LoggerFactory;
12  
13  import javax.servlet.http.HttpServletRequest;
14  import javax.servlet.http.HttpServletResponse;
15  import java.io.IOException;
16  import java.io.OutputStream;
17  import java.util.Map;
18  
19  import static com.google.common.collect.Iterables.any;
20  
21  /**
22   * A base class that batches together a sequence of DownloableResources
23   *
24   * @since 2.13
25   */
26  public abstract class AbstractBatchDownloadableResource implements DownloadableResource
27  {
28      private static final Logger log = LoggerFactory.getLogger(AbstractBatchDownloadableResource.class);
29      
30      private static final ResourceContentAnnotator[] DEFAULT_ANNOTATORS = new ResourceContentAnnotator[] {
31          new NewlineResourceContentAnnotator()
32      };
33      
34      private static final ResourceContentAnnotator[] JS_WRAP_ANNOTATORS = new ResourceContentAnnotator[] {
35          new NewlineResourceContentAnnotator(), new TryCatchJsResourceContentAnnotator()
36      };
37      
38      private final String type;
39      private final Map<String, String> params;
40      private final Iterable<DownloadableResource> resources;
41      private final CacheHandle cacher;
42      private final ResourceContentAnnotator[] resourceContentAnnotators;
43  
44      /**
45       * This constructor should only ever be used internally within this class. It does not ensure that the resourceName's
46       * file extension is the same as the given type. It is up to the calling code to ensure this.
47       * @param type - the type of resource (CSS/JS)
48       * @param params - the parameters of the resource (ieonly, media, etc)
49       * @param resources - the resources included in the batch.
50       */
51      AbstractBatchDownloadableResource(final String type, final Map<String, String> params,
52                                        final Iterable<DownloadableResource> resources,
53                                        CacheHandle cacher)
54      {
55          this.type = type;
56          this.params = ImmutableMap.copyOf(params);
57          this.resources = resources;
58          this.cacher = cacher;
59          this.resourceContentAnnotators = getAnnotators(); 
60      }
61  
62      /**
63       * @return true if there are no resources included in this batch
64       */
65      public boolean isEmpty()
66      {
67          return Iterables.isEmpty(resources);
68      }
69  
70      public boolean isResourceModified(final HttpServletRequest request, final HttpServletResponse response)
71      {
72          return any(resources, new Predicate<DownloadableResource>()
73          {
74              public boolean apply(final DownloadableResource resource)
75              {
76                  return resource.isResourceModified(request, response);
77              }
78          });
79      }
80  
81      public void serveResource(final HttpServletRequest request, final HttpServletResponse response) throws DownloadException
82      {
83          if (log.isDebugEnabled())
84          {
85              log.debug("Start to serve batch " + toString());
86          }
87          
88          OutputStream out;
89          try
90          {
91              out = response.getOutputStream();
92          }
93          catch (final IOException e)
94          {
95              throw new DownloadException(e);
96          }
97  
98          streamResourceInternal(out, resourceContentAnnotators);
99      }
100 
101     public void streamResource(final OutputStream originalOut) throws DownloadException
102     {
103         streamResourceInternal(originalOut,resourceContentAnnotators);
104     }
105     
106     private void streamResourceInternal(final OutputStream originalOut, final ResourceContentAnnotator[] annotators) throws DownloadException
107     {
108         final FileCacheStreamProvider streamProvider = new FileCacheStreamProvider() {
109             @Override
110             public void writeStream(OutputStream dest) throws DownloadException {
111                 for (final DownloadableResource resource : resources)
112                 {
113                     try
114                     {
115                         applyBeforeAnnotators(dest, annotators);                    
116                         resource.streamResource(dest);
117                         applyAfterAnnotators(dest, annotators);
118                     }
119                     catch (IOException ex)
120                     {
121                         throw new DownloadException(ex);
122                     }
123                 }
124             }
125         };
126         cacher.stream(originalOut, streamProvider);
127     }
128 
129     public String getContentType()
130     {
131         final String contentType = params.get("content-type");
132         if (contentType != null)
133         {
134             return contentType;
135         }
136         return null;
137     }
138 
139     public Map<String, String> getParams()
140     {
141         return params;
142     }
143 
144     public String getType()
145     {
146         return type;
147     }
148     
149     private void applyBeforeAnnotators(OutputStream str, ResourceContentAnnotator[] annotators) throws IOException
150     {
151         for (ResourceContentAnnotator annotator : annotators)
152         {
153             annotator.before(str);
154         }
155     }
156     
157     /**
158      * Apply the after annotators in reverse order.
159      * 
160      * @param str
161      * @throws IOException
162      */
163     private void applyAfterAnnotators(OutputStream str, ResourceContentAnnotator[] annotators) throws IOException
164     {
165         for (int i = annotators.length - 1; i >= 0; i--)
166         {
167             annotators[i].after(str);
168         }
169     }
170     
171     /**
172      * @return the annotators to use when writing the requested Batch resource.
173      */
174     private ResourceContentAnnotator[] getAnnotators() 
175     {
176         if ("js".equals(this.getType()))
177         {
178             String trycatch = System.getProperty(DefaultResourceBatchingConfiguration.PLUGIN_WEBRESOURCE_TRY_CATCH_WRAPPING);
179             if (trycatch != null && Boolean.parseBoolean(trycatch))
180             {
181                 return JS_WRAP_ANNOTATORS;
182             }
183             else
184             {
185                 return DEFAULT_ANNOTATORS;
186             }
187         }
188         else
189         {
190             return DEFAULT_ANNOTATORS;
191         }
192     }
193 }