1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.trinidad.resource;
20
21 import java.io.InputStream;
22 import java.io.SequenceInputStream;
23 import java.net.URL;
24
25 import java.net.URLConnection;
26 import java.net.URLStreamHandler;
27 import java.util.ArrayList;
28
29 import java.io.IOException;
30 import java.util.Enumeration;
31 import java.util.NoSuchElementException;
32
33 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
34 import org.apache.myfaces.trinidad.util.URLUtils;
35
36
37
38
39
40
41 public class AggregatingResourceLoader extends DynamicResourceLoader
42 {
43
44
45
46
47
48
49
50
51 public AggregatingResourceLoader(
52 String path,
53 String[] paths,
54 ResourceLoader target,
55 ResourceLoader parent)
56 {
57 super(path, parent);
58 if (paths == null)
59 throw new NullPointerException();
60
61 if (target == null)
62 throw new NullPointerException();
63
64 _paths = paths.clone();
65 _target = target;
66 }
67
68
69
70
71
72
73
74
75 public AggregatingResourceLoader(
76 String path,
77 String[] paths,
78 ResourceLoader target)
79 {
80 this(path, paths, target, null);
81 }
82
83
84
85
86
87
88
89 public void setSeparator(String separator)
90 {
91 _separator = separator;
92 }
93
94
95
96
97
98
99
100
101 @Override
102 protected URL getURL(String path) throws IOException
103 {
104 int len = _paths.length;
105 ArrayList<URL> urls = new ArrayList<URL>(len);
106 for(int i = 0; i < len; i++)
107 {
108 URL u = _target.getResource(_paths[i]);
109 if(u != null)
110 {
111 urls.add(u);
112 }
113 else
114 {
115 _LOG.warning("RESOURCE_NOT_FOUND", new Object[]{_paths[i], path});
116 }
117 }
118
119 urls.trimToSize();
120 URL[] urlArray = urls.toArray(new URL[urls.size()]);
121
122 AggregatingURLStreamHandler handler = new AggregatingURLStreamHandler(urlArray, _separator);
123 return new URL("aggregating", null, -1, path, handler);
124 }
125
126 private String[] _paths;
127 private ResourceLoader _target;
128 private String _separator;
129 static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(AggregatingResourceLoader.class);
130
131
132
133
134
135
136 public class AggregatingURLStreamHandler extends URLStreamHandler
137 {
138
139
140
141
142
143
144 public AggregatingURLStreamHandler(URL[] urls)
145 {
146 this(urls, null);
147 }
148
149
150
151
152
153
154
155
156 public AggregatingURLStreamHandler(URL[] urls, String separator)
157 {
158 if(urls == null)
159 {
160 throw new NullPointerException();
161 }
162 _urls = urls.clone();
163 _separator = separator;
164 }
165
166
167
168
169
170
171
172
173
174 @Override
175 protected URLConnection openConnection(URL u) throws IOException
176 {
177 int len = _urls.length;
178 URLConnection[] conn = new URLConnection[len];
179 for(int i = 0; i < len; i++)
180 {
181 conn[i] = _urls[i].openConnection();
182 }
183
184 return new AggregatingURLConnection(u, conn, _separator);
185 }
186
187 private URL[] _urls;
188 private String _separator;
189
190 }
191
192 static private class AggregatingURLConnection extends URLConnection
193 {
194 public AggregatingURLConnection(URL url, URLConnection[] connections, String separator)
195 {
196 super(url);
197 _connections = connections;
198
199 if(separator != null)
200 {
201 _separator = separator.getBytes();
202 }
203 }
204
205 @Override
206 public void connect() throws IOException
207 {
208 for (int i=0, len = _connections.length; i < len; i++)
209 {
210 _connections[i].connect();
211 }
212 }
213
214 @Override
215 public InputStream getInputStream() throws IOException
216 {
217 boolean hasseparator = (_separator!=null);
218 InputStream[] streams;
219 if(hasseparator)
220 {
221 streams = new InputStream[(_connections.length *2)-1];
222 }
223 else
224 {
225 streams = new InputStream[_connections.length];
226 }
227
228 for (int i=0, len=_connections.length, sublen = len -1, streamCounter = 0; i < len; i++, streamCounter++)
229 {
230 streams[streamCounter] = _connections[i].getInputStream();
231
232
233 if(hasseparator && (i < sublen))
234 {
235 streams[++streamCounter] = new SeparatorInputStream(_separator);
236 }
237 }
238 return new SequenceInputStream(new ArrayEnumeration<InputStream>(streams));
239 }
240
241 @Override
242 public String getContentType()
243 {
244 return _connections[0].getContentType();
245 }
246
247 @Override
248 public int getContentLength()
249 {
250 int totalContentLength = _contentLength;
251
252
253
254 if (totalContentLength == Integer.MIN_VALUE)
255 {
256 totalContentLength = 0;
257
258 URLConnection[] connects = _connections;
259
260
261
262
263 if (_separator != null)
264 {
265 totalContentLength += ((connects.length - 1)*_separator.length);
266 }
267
268 for (int i=0, len = _connections.length; i < len; i++)
269 {
270 int contentLength = _connections[i].getContentLength();
271 if (contentLength < 0)
272 {
273
274
275 totalContentLength = -1;
276 break;
277 }
278 totalContentLength += contentLength;
279 }
280
281 _contentLength = totalContentLength;
282 }
283
284 return totalContentLength;
285 }
286
287 @Override
288 public long getLastModified()
289 {
290 long maxLastModified = -1;
291
292 for (int i=0, len = _connections.length; i < len; i++)
293 {
294 long lastModified;
295 try
296 {
297 lastModified = URLUtils.getLastModified(_connections[i]);
298 }
299 catch (IOException exception)
300 {
301 maxLastModified = -1;
302 break;
303 }
304
305 if (lastModified < 0)
306 {
307 maxLastModified = lastModified;
308 break;
309 }
310 maxLastModified = Math.max(maxLastModified, lastModified);
311 }
312
313 return maxLastModified;
314 }
315
316 @Override
317 public String getHeaderField(
318 String name)
319 {
320 if ("content-length".equals(name))
321 {
322 return String.valueOf(getContentLength());
323 }
324 else if ("content-type".equals(name))
325 {
326 return getContentType();
327 }
328 else if ("last-modified".equals(name))
329 {
330 return String.valueOf(getLastModified());
331 }
332 else
333 {
334 return super.getHeaderField(name);
335 }
336 }
337
338 private int _contentLength = Integer.MIN_VALUE;
339 private URLConnection[] _connections;
340 private byte[] _separator;
341
342 }
343
344 static private class ArrayEnumeration<T> implements Enumeration<T>
345 {
346 public ArrayEnumeration(T[] array)
347 {
348 _array = array;
349 _len = array.length;
350 }
351
352 public boolean hasNext()
353 {
354 return _pointer < _len;
355 }
356
357 public T nextElement() throws NoSuchElementException
358 {
359 try
360 {
361 return _array[_pointer++];
362 }
363 catch (IndexOutOfBoundsException e)
364 {
365 throw new NoSuchElementException();
366 }
367 }
368
369 public boolean hasMoreElements()
370 {
371 return hasNext();
372 }
373
374 private T[] _array;
375 private int _len;
376 private int _pointer = 0;
377 }
378
379 static private class SeparatorInputStream extends InputStream
380 {
381 public SeparatorInputStream (byte[] separatorBytes)
382 {
383 _separator = separatorBytes;
384 _length = _separator.length;
385 }
386
387 @Override
388 public int read() throws IOException
389 {
390 if(_index < _length)
391 {
392 return _separator[_index++];
393 }
394
395 return -1;
396 }
397
398 @Override
399 public int read(byte[] b, int off, int len) throws IOException
400 {
401 int bytesLeft = available();
402
403 if(len <= bytesLeft)
404 {
405 System.arraycopy(_separator,_index,b,off,len);
406 _index += len;
407 return len;
408 }
409 else
410 {
411 System.arraycopy(_separator, _index, b,off, bytesLeft);
412 _index += bytesLeft;
413 return bytesLeft;
414 }
415 }
416
417 @Override
418 public long skip(long n) throws IOException
419 {
420 int bytesLeft = available();
421
422 if(n < 0)
423 {
424 return 0;
425 }
426 else if(n < bytesLeft || n == bytesLeft)
427 {
428 _index += n;
429 return n;
430 }
431 else
432 {
433 _index += bytesLeft;
434 return bytesLeft;
435 }
436 }
437
438 @Override
439 public int available() throws IOException
440 {
441 return _length - _index;
442 }
443
444 private byte[] _separator;
445 private int _length;
446 private int _index = 0;
447 }
448 }