1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.commons.resourcehandler;
20
21 import java.io.BufferedInputStream;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.net.MalformedURLException;
30 import java.net.URL;
31 import java.util.Map;
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.zip.GZIPOutputStream;
34
35 import javax.faces.FacesException;
36 import javax.faces.context.FacesContext;
37
38 import org.apache.myfaces.commons.resourcehandler.resource.ResourceLoader;
39 import org.apache.myfaces.commons.resourcehandler.resource.ResourceLoaderWrapper;
40 import org.apache.myfaces.commons.resourcehandler.resource.ResourceMeta;
41 import org.apache.myfaces.commons.resourcehandler.resource.ValueExpressionFilterInputStream;
42
43
44
45
46
47
48
49 public class GZIPResourceLoader extends ResourceLoaderWrapper
50 {
51
52 public final static String COMPRESSED_FILES_MAP = "oam.commons.COMPRESSED_FILES_MAP";
53
54
55
56
57 private static final String COMPRESSION_BASE_DIR = "oam-resourcehandler-cache/";
58
59
60
61
62 private static final String COMPRESSED_FILE_SUFFIX = ".gzip";
63
64
65
66
67 private static final int BUFFER_SIZE = 2048;
68
69 private ResourceLoader delegate;
70
71 private volatile File _tempDir;
72
73 private final ExtendedDefaultResourceHandlerSupport _extendedDefaultResourceHandlerSupport;
74
75 public GZIPResourceLoader(ResourceLoader delegate, ExtendedDefaultResourceHandlerSupport extendedDefaultResourceHandlerSupport)
76 {
77 this.delegate = delegate;
78 _extendedDefaultResourceHandlerSupport = extendedDefaultResourceHandlerSupport;
79 initialize();
80 }
81
82 protected void initialize()
83 {
84
85 FacesContext facesContext = FacesContext.getCurrentInstance();
86
87
88 Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
89 File tempdir = (File) applicationMap.get("javax.servlet.context.tempdir");
90 File imagesDir = new File(tempdir, COMPRESSION_BASE_DIR);
91 if (!imagesDir.exists())
92 {
93 imagesDir.mkdirs();
94 }
95 else
96 {
97
98 deleteDir(imagesDir);
99 imagesDir.mkdirs();
100 }
101 _tempDir = imagesDir;
102
103
104 Map<String, FileProducer> compressedFilesMap = new ConcurrentHashMap<String, FileProducer>();
105 facesContext.getExternalContext().getApplicationMap().put(COMPRESSED_FILES_MAP, compressedFilesMap);
106 }
107
108 private static boolean deleteDir(File dir)
109 {
110 if (dir.isDirectory())
111 {
112 String[] children = dir.list();
113 for (int i = 0; i < children.length; i++)
114 {
115 boolean success = deleteDir(new File(dir, children[i]));
116 if (!success)
117 {
118 return false;
119 }
120 }
121 }
122 return dir.delete();
123 }
124
125 @Override
126 public URL getResourceURL(ResourceMeta resourceMeta)
127 {
128 FacesContext facesContext = FacesContext.getCurrentInstance();
129
130 if (!_extendedDefaultResourceHandlerSupport.isCompressable(resourceMeta) || !_extendedDefaultResourceHandlerSupport.userAgentSupportsCompression(facesContext))
131 {
132 return super.getResourceURL(resourceMeta);
133 }
134
135 if (resourceExists(resourceMeta))
136 {
137 File file = createOrGetCompressedFile(facesContext, resourceMeta);
138
139 try
140 {
141 return file.toURL();
142 }
143 catch (MalformedURLException e)
144 {
145 throw new FacesException(e);
146 }
147 }
148 else
149 {
150 return null;
151 }
152 }
153
154 @Override
155 public InputStream getResourceInputStream(ResourceMeta resourceMeta)
156 {
157 FacesContext facesContext = FacesContext.getCurrentInstance();
158
159 if (!_extendedDefaultResourceHandlerSupport.isCompressable(resourceMeta) || !_extendedDefaultResourceHandlerSupport.userAgentSupportsCompression(facesContext))
160 {
161 return super.getResourceInputStream(resourceMeta);
162 }
163
164 if (resourceExists(resourceMeta))
165 {
166 File file = createOrGetCompressedFile(facesContext, resourceMeta);
167
168 try
169 {
170 return new BufferedInputStream(new FileInputStream(file));
171 }
172 catch (FileNotFoundException e)
173 {
174 throw new FacesException(e);
175 }
176 }
177 else
178 {
179 return null;
180 }
181 }
182
183 @Override
184 public boolean resourceExists(ResourceMeta resourceMeta)
185 {
186 return super.resourceExists(resourceMeta);
187 }
188
189 @SuppressWarnings("unchecked")
190 private File createOrGetCompressedFile(FacesContext facesContext, ResourceMeta resourceMeta)
191 {
192 String identifier = resourceMeta.getResourceIdentifier();
193 File file = getCompressedFile(resourceMeta);
194 if (!file.exists())
195 {
196 Map<String, FileProducer> map = (Map<String, FileProducer>)
197 facesContext.getExternalContext().getApplicationMap().get(COMPRESSED_FILES_MAP);
198
199 FileProducer creator = map.get(identifier);
200
201 if (creator == null)
202 {
203 synchronized(this)
204 {
205 creator = map.get(identifier);
206
207 if (creator == null)
208 {
209 creator = new FileProducer();
210 map.put(identifier, creator);
211 }
212 }
213 }
214
215 if (!creator.isCreated())
216 {
217 creator.createFile(facesContext, resourceMeta, file, this);
218 }
219 }
220 return file;
221 }
222
223 private File getCompressedFile(ResourceMeta resourceMeta)
224 {
225 return new File(_tempDir, resourceMeta.getResourceIdentifier() + COMPRESSED_FILE_SUFFIX);
226 }
227
228 private boolean couldResourceContainValueExpressions(ResourceMeta resourceMeta)
229 {
230 return resourceMeta.couldResourceContainValueExpressions() || resourceMeta.getResourceName().endsWith(".css");
231 }
232
233
234
235
236
237
238
239
240
241
242 protected void createCompressedFileVersion(FacesContext facesContext, ResourceMeta resourceMeta, File target)
243 {
244
245 target.mkdirs();
246 target.delete();
247
248 InputStream inputStream = null;
249 FileOutputStream fileOutputStream;
250 GZIPOutputStream gzipOutputStream = null;
251 try
252 {
253 if (couldResourceContainValueExpressions(resourceMeta))
254 {
255 inputStream = new ValueExpressionFilterInputStream(
256 getWrapped().getResourceInputStream(resourceMeta),
257 resourceMeta.getLibraryName(),
258 resourceMeta.getResourceName());
259 }
260 else
261 {
262 inputStream = getWrapped().getResourceInputStream(resourceMeta);
263 }
264 fileOutputStream = new FileOutputStream(target);
265 gzipOutputStream = new GZIPOutputStream(fileOutputStream);
266 byte[] buffer = new byte[BUFFER_SIZE];
267
268 pipeBytes(inputStream, gzipOutputStream, buffer);
269 }
270 catch (FileNotFoundException e)
271 {
272 throw new FacesException("Unexpected exception while create file:", e);
273 }
274 catch (IOException e)
275 {
276 throw new FacesException("Unexpected exception while create file:", e);
277 }
278 finally
279 {
280 if (inputStream != null)
281 {
282 try
283 {
284 inputStream.close();
285 }
286 catch (IOException e)
287 {
288
289 }
290 }
291 if (gzipOutputStream != null)
292 {
293
294 try
295 {
296 gzipOutputStream.close();
297 }
298 catch (IOException e)
299 {
300
301 }
302 }
303 }
304 }
305
306
307
308
309
310 private static void pipeBytes(InputStream in, OutputStream out, byte[] buffer) throws IOException
311 {
312 int length;
313
314 while ((length = (in.read(buffer))) >= 0)
315 {
316 out.write(buffer, 0, length);
317 }
318 }
319
320 public static class FileProducer {
321
322 public volatile boolean created = false;
323
324 public FileProducer()
325 {
326 super();
327 }
328
329 public boolean isCreated()
330 {
331 return created;
332 }
333
334 public synchronized void createFile(FacesContext facesContext, ResourceMeta resourceMeta, File file, GZIPResourceLoader loader)
335 {
336 if (!created)
337 {
338 loader.createCompressedFileVersion(facesContext, resourceMeta, file);
339 created = true;
340 }
341 }
342 }
343
344 public ResourceLoader getWrapped()
345 {
346 return delegate;
347 }
348 }