1
2
3
4
5 package org.rcfaces.core.internal.repository;
6
7 import java.io.BufferedInputStream;
8 import java.io.CharArrayReader;
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.InputStreamReader;
12 import java.io.UnsupportedEncodingException;
13 import java.net.MalformedURLException;
14 import java.net.URL;
15 import java.net.URLConnection;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.StringTokenizer;
23 import java.util.zip.GZIPOutputStream;
24
25 import javax.servlet.ServletConfig;
26 import javax.servlet.ServletException;
27
28 import org.apache.commons.digester.Digester;
29 import org.apache.commons.digester.Rule;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.rcfaces.core.internal.RcfacesContext;
33 import org.rcfaces.core.internal.codec.SourceFilter;
34 import org.rcfaces.core.internal.lang.ByteBufferOutputStream;
35 import org.rcfaces.core.internal.lang.StringAppender;
36 import org.rcfaces.core.internal.webapp.ExtendedHttpServlet;
37 import org.xml.sax.Attributes;
38 import org.xml.sax.EntityResolver;
39 import org.xml.sax.InputSource;
40
41
42
43
44
45
46 public abstract class SourceContainer {
47 private static final String REVISION = "$Revision: 1.3 $";
48
49 private static final Log LOG = LogFactory.getLog(SourceContainer.class);
50
51 protected static final String EXTERNAL_REPOSITORIES_CONFIG_NAME = "external.repositories";
52
53 private static final int BUFFER_INITIAL_SIZE = 16000;
54
55 private static final String NO_PARAMETER = "~#NO_PARAMETER#~";
56
57 private final ServletConfig servletConfig;
58
59 private final String charSet;
60
61 private final boolean canUseGzip;
62
63 private final boolean canUseETag;
64
65 private final boolean canUseHash;
66
67 private final Set modules;
68
69 private final String externalRepositoriesPropertyName;
70
71 private final String repositoryVersion;
72
73 private final String repositoryType;
74
75 private final Map contentByParameters = new HashMap();
76
77
78
79 public SourceContainer(ServletConfig config, String repositoryType,
80 Set modules, String charSet, boolean canUseGzip,
81 boolean canUseETag, boolean canUseHash,
82 String externalRepositoriesPropertyName, String repositoryVersion)
83 throws ServletException {
84 this.servletConfig = config;
85 this.repositoryType = repositoryType;
86 this.canUseGzip = canUseGzip;
87 this.canUseETag = canUseETag;
88 this.canUseHash = canUseHash;
89 this.charSet = charSet;
90 this.modules = modules;
91 this.repositoryVersion = repositoryVersion;
92 this.externalRepositoriesPropertyName = externalRepositoriesPropertyName;
93
94 flush();
95 }
96
97 public final String getCharSet() {
98 return charSet;
99 }
100
101 public final boolean isCanUseGzip() {
102 return canUseGzip;
103 }
104
105 public final boolean isCanUseETag() {
106 return canUseETag;
107 }
108
109 public final boolean isCanUseHash() {
110 return canUseHash;
111 }
112
113 protected StringAppender postConstructBuffer(
114 BasicParameterizedContent parameterizedBuffer, StringAppender buffer) {
115
116 if (canSkipSpace()) {
117 return new StringAppender(SourceFilter.filterSkipSpaces(buffer
118 .toString()));
119 }
120
121 if (canRemoveComments()) {
122 return new StringAppender(SourceFilter.filter(buffer.toString()));
123 }
124
125 return buffer;
126 }
127
128 protected StringAppender preConstructBuffer(
129 BasicParameterizedContent parameterizedBuffer, StringAppender buffer) {
130 return buffer;
131 }
132
133 public String getVersion() {
134 return repositoryVersion;
135 }
136
137 protected URL getURL(String path) {
138 if (LOG.isDebugEnabled()) {
139 LOG.debug("Getting URL of '" + path + "'");
140 }
141
142 String pr = path;
143 if (pr.charAt(0) != '/') {
144 pr = "/" + pr;
145 }
146
147 try {
148 URL url = servletConfig.getServletContext().getResource(pr);
149
150 if (LOG.isDebugEnabled()) {
151 LOG.debug("URL resource of servletContext '" + path + "' => "
152 + url);
153 }
154
155 if (url != null) {
156 try {
157 InputStream in = url.openStream();
158
159 if (LOG.isDebugEnabled()) {
160 LOG.debug("Open stream resource of servletContext '"
161 + url + "' => " + in);
162 }
163
164 if (in != null) {
165 try {
166 in.close();
167
168 } catch (IOException ex) {
169
170 }
171
172 return url;
173 }
174 } catch (IOException ex) {
175
176 if (LOG.isDebugEnabled()) {
177 LOG.debug("IOException for servletContext resource '"
178 + url + "'", ex);
179 }
180 }
181 }
182
183 } catch (MalformedURLException e) {
184
185 if (LOG.isDebugEnabled()) {
186 LOG.debug("Malformed url for servletContext '" + path + "'", e);
187 }
188 }
189
190 pr = path;
191 if (pr.charAt(0) == '/') {
192 pr = pr.substring(1);
193 }
194 URL url = getClass().getClassLoader().getResource(pr);
195
196 if (LOG.isDebugEnabled()) {
197 LOG.debug("URL resource of classLoader '" + path + "' => " + url);
198 }
199
200 if (url != null) {
201 try {
202 InputStream in = url.openStream();
203
204 if (LOG.isDebugEnabled()) {
205 LOG.debug("Open stream resource of classLoader '" + url
206 + "' => " + in);
207 }
208
209 if (in != null) {
210 try {
211 in.close();
212
213 } catch (IOException ex) {
214
215 }
216
217 return url;
218 }
219 } catch (IOException ex) {
220
221 if (LOG.isDebugEnabled()) {
222 LOG.debug("IOException for classLoader resource '" + url
223 + "'", ex);
224 }
225 }
226 }
227
228 if (LOG.isDebugEnabled()) {
229 LOG.debug("No URL found for path '" + path + "'");
230 }
231
232 return null;
233 }
234
235 private List listExternalFiles(InputStream inputStream, String source,
236 boolean nameAttribute) {
237
238 Digester digester = new Digester();
239 digester.setUseContextClassLoader(true);
240
241 digester.setEntityResolver(new EntityResolver() {
242 private static final String REVISION = "$Revision: 1.3 $";
243
244 public InputSource resolveEntity(String string, String string1) {
245 return new InputSource(new CharArrayReader(new char[0]));
246 }
247
248 });
249
250 final List list = new ArrayList();
251
252 if (nameAttribute) {
253 final String[] baseDirectoryRef = new String[1];
254
255 digester.addRule("repository", new Rule() {
256
257 public void begin(String namespace, String name,
258 Attributes attributes) {
259
260 String baseDirectory = attributes.getValue("baseDirectory");
261
262 if (baseDirectory != null && baseDirectory.length() > 0) {
263 baseDirectoryRef[0] = baseDirectory;
264 }
265 }
266 });
267
268 final boolean[] filtred = new boolean[1];
269
270 digester.addRule("repository/module", new Rule() {
271
272 public void begin(String namespace, String name,
273 Attributes attributes) {
274
275 String id = attributes.getValue("id");
276
277 if (modules != null) {
278 filtred[0] = (id != null && modules.contains(id) == false);
279
280 } else {
281 filtred[0] = false;
282 }
283 }
284 });
285
286 digester.addRule("repository/module/file", new Rule() {
287
288 public void begin(String namespace, String name,
289 Attributes attributes) {
290
291 if (filtred[0]) {
292 return;
293 }
294
295 SourceFile file = createSourceFile(baseDirectoryRef[0],
296 null, attributes);
297 if (file != null) {
298 list.add(file);
299 }
300 }
301
302 });
303
304 } else {
305 digester.addRule("repository/file", new Rule() {
306
307 public void body(String namespace, String name, String text) {
308 SourceFile file = createSourceFile(null, text, null);
309 if (file != null) {
310 list.add(file);
311 }
312 }
313 });
314 }
315
316 try {
317 digester.parse(inputStream);
318
319 } catch (Exception e) {
320 LOG.error("Can not parse '" + source + "'", e);
321 }
322
323 return list;
324 }
325
326 protected SourceFile createSourceFile(String baseDirectory, String body,
327 Attributes attributes) {
328
329 if (attributes != null) {
330 String fileName = attributes.getValue("name");
331
332 if (fileName != null && fileName.length() > 0) {
333 if (baseDirectory != null) {
334 fileName = baseDirectory + "/" + fileName;
335 }
336
337 SourceFile sourceFile = newSourceFile();
338
339 sourceFile.setFileName(fileName);
340
341 return sourceFile;
342 }
343
344 return null;
345 }
346
347 if (body != null && body.trim().length() > 0) {
348 String fileName = body.trim();
349 if (baseDirectory != null) {
350 fileName = baseDirectory + "/" + fileName;
351 }
352
353 SourceFile sourceFile = newSourceFile();
354
355 sourceFile.setFileName(fileName);
356
357 return sourceFile;
358 }
359
360 return null;
361 }
362
363 protected SourceFile newSourceFile() {
364 return new SourceFile();
365 }
366
367 protected boolean canSkipSpace() {
368 return false;
369 }
370
371 protected boolean canRemoveComments() {
372 return false;
373 }
374
375 public synchronized void flush() throws ServletException {
376 synchronized (contentByParameters) {
377 contentByParameters.clear();
378 }
379 }
380
381 protected void error(String message, Throwable th) {
382 message = "RCFaces.SourceRepository: " + message;
383
384 LOG.error(message, th);
385
386 if (th == null) {
387 servletConfig.getServletContext().log(message);
388 return;
389 }
390
391 servletConfig.getServletContext().log(message, th);
392 }
393
394 public IParameterizedContent getDefaultContent() throws ServletException {
395 return getContent(NO_PARAMETER);
396 }
397
398 public IParameterizedContent getContent(String parameter)
399 throws ServletException {
400
401 IParameterizedContent parameterizedContent;
402 synchronized (contentByParameters) {
403 parameterizedContent = (IParameterizedContent) contentByParameters
404 .get(parameter);
405
406 if (parameterizedContent == null) {
407 parameterizedContent = createParameterizedContent((parameter != NO_PARAMETER) ? parameter
408 : null);
409
410 contentByParameters.put(parameter, parameterizedContent);
411 }
412 }
413
414 parameterizedContent.initialize();
415
416 return parameterizedContent;
417 }
418
419 protected IParameterizedContent createParameterizedContent(String parameter) {
420 return new BasicParameterizedContent(parameter);
421 }
422
423 protected void addURLContent(URLConnection urlConnection,
424 StringAppender buffer) throws IOException {
425
426 InputStream inputStream = urlConnection.getInputStream();
427
428 try {
429 char buf[] = new char[4096];
430
431 InputStreamReader inr = new InputStreamReader(
432 new BufferedInputStream(inputStream, buf.length), charSet);
433
434 for (;;) {
435 int len = inr.read(buf, 0, buf.length);
436 if (len < 1) {
437 break;
438 }
439
440 buffer.append(buf, 0, len);
441 }
442 } finally {
443 inputStream.close();
444 }
445 }
446
447
448
449
450
451
452 public interface IParameterizedContent {
453
454 SourceContainer getContainer();
455
456 void initialize() throws ServletException;
457
458 long getLastModified();
459
460 String getETag();
461
462 String getHash();
463
464 byte[] getRawBuffer();
465
466 byte[] getGZipedBuffer();
467 }
468
469
470
471
472
473
474 protected class BasicParameterizedContent implements IParameterizedContent {
475 protected final String parameter;
476
477 private boolean initialized;
478
479 private StringAppender buffer;
480
481 private byte[] sourceBuffer = null;
482
483 private byte[] sourceBufferGZip = null;
484
485 private long lastModified = -1;
486
487 private String etag = null;
488
489 private String hash = null;
490
491 public BasicParameterizedContent(String parameter) {
492 this.parameter = parameter;
493 }
494
495 public synchronized void initialize() throws ServletException {
496 if (initialized) {
497 return;
498 }
499 initialized = true;
500
501 buffer = new StringAppender(BUFFER_INITIAL_SIZE);
502
503 buffer = preConstructBuffer(this, buffer);
504
505 addRepositoryFiles();
506
507 if (externalRepositoriesPropertyName != null) {
508 buffer = addExternalRepositories(buffer,
509 externalRepositoriesPropertyName);
510 }
511
512 buffer = postConstructBuffer(this, buffer);
513
514 initializeBuffers();
515
516 buffer = null;
517 }
518
519 public SourceContainer getContainer() {
520 return SourceContainer.this;
521 }
522
523 protected void updateLastModification(URLConnection urlConnection) {
524 long lm = urlConnection.getLastModified();
525 if (lm <= 0) {
526 return;
527 }
528
529 if (lm <= lastModified) {
530 return;
531 }
532
533 lastModified = lm;
534 }
535
536 protected void initializeBuffers() throws ServletException {
537
538 try {
539 sourceBuffer = buffer.getBytes(getCharSet());
540
541 } catch (UnsupportedEncodingException e) {
542 LOG.error(e);
543 throw new ServletException(e);
544 }
545
546 if (lastModified <= 0) {
547 lastModified = System.currentTimeMillis();
548 }
549
550 if (canUseETag) {
551 etag = ExtendedHttpServlet.computeETag(sourceBuffer);
552 }
553
554 if (canUseHash) {
555 hash = ExtendedHttpServlet.computeHash(sourceBuffer);
556 }
557
558 if (canUseGzip && sourceBuffer.length > 0) {
559 try {
560 ByteBufferOutputStream bos = new ByteBufferOutputStream(
561 sourceBuffer.length);
562 GZIPOutputStream gzos = new GZIPOutputStream(bos,
563 sourceBuffer.length);
564 gzos.write(sourceBuffer);
565 gzos.close();
566
567 sourceBufferGZip = bos.toByteArray();
568
569 bos.close();
570
571 } catch (IOException ex) {
572 throw new ServletException(
573 "Can not create GZIP buffer for " + repositoryType
574 + " files.", ex);
575 }
576 }
577
578 if (LOG.isInfoEnabled()) {
579 String message = repositoryType + ": buffers loaded into "
580 + sourceBuffer.length + " bytes";
581 if (sourceBufferGZip != null) {
582 message += " (GZiped: "
583 + sourceBufferGZip.length
584 + " ["
585 + (sourceBufferGZip.length * 100 / sourceBuffer.length)
586 + "%])";
587 }
588
589 LOG.info(message, null);
590 }
591 }
592
593 protected final StringAppender addExternalRepositories(
594 StringAppender buffer, String propertName) {
595 if (propertName == null) {
596 return buffer;
597 }
598
599 String path = servletConfig.getInitParameter(propertName);
600 if (path == null) {
601 return buffer;
602 }
603
604 StringTokenizer st = new StringTokenizer(path, ";,");
605 for (; st.hasMoreTokens();) {
606 String repositoryPath = st.nextToken();
607
608 parseXMLRepository(repositoryPath, false);
609 }
610
611 return buffer;
612 }
613
614 protected final void parseXMLRepository(String path,
615 boolean repositoryFormal) {
616 URL url = getURL(path);
617 if (url == null) {
618 error("Can not get URL for path '" + path + "'.", null);
619 return;
620 }
621
622 URLConnection urlConnection;
623 try {
624 urlConnection = url.openConnection();
625
626 } catch (IOException ex) {
627 error("Can not get content of '" + url + "'.", ex);
628
629 return;
630 }
631
632 if (urlConnection == null) {
633 return;
634 }
635
636 updateLastModification(urlConnection);
637
638 InputStream inputStream;
639 try {
640 inputStream = urlConnection.getInputStream();
641
642 } catch (IOException ex) {
643 error("Can not open '" + url + "'.", ex);
644
645 return;
646 }
647
648 if (inputStream == null) {
649 return;
650 }
651
652 List files;
653 try {
654 files = listExternalFiles(inputStream, path, repositoryFormal);
655
656 } finally {
657 try {
658 inputStream.close();
659 } catch (IOException e) {
660 }
661 }
662
663 if (files != null) {
664 files = filterFiles(files);
665 }
666
667 if (files == null || files.isEmpty() == true) {
668 return;
669 }
670
671 for (Iterator it = files.iterator(); it.hasNext();) {
672 SourceFile sourceFile = (SourceFile) it.next();
673
674 URL fileURL = getURL(sourceFile.getFileName());
675 if (fileURL == null) {
676 error("Can not get URL for path '" + sourceFile + "'.",
677 null);
678 continue;
679 }
680
681 try {
682 addURLContent(fileURL, buffer);
683
684 } catch (IOException e) {
685 error("Can not append external file '" + fileURL + "'.", e);
686 }
687 }
688 }
689
690 protected void addURLContent(URL url, StringAppender buffer)
691 throws IOException {
692
693 URLConnection urlConnection = url.openConnection();
694
695 LOG.debug("Load URL content '" + url + "' (charset=" + charSet
696 + ").");
697
698 updateLastModification(urlConnection);
699
700 SourceContainer.this.addURLContent(urlConnection, buffer);
701 }
702
703 protected List filterFiles(List files) {
704 return files;
705 }
706
707 protected void addRepositoryFiles() throws ServletException {
708
709 RcfacesContext rcfacesContext = RcfacesContext.getInstance(
710 servletConfig.getServletContext(), null, null);
711
712 String repositoriesPaths[] = rcfacesContext.getRepositoryManager()
713 .listRepositoryLocations(repositoryType);
714
715 for (int j = 0; j < repositoriesPaths.length; j++) {
716 String repositoryPath = repositoriesPaths[j];
717
718 parseXMLRepository(repositoryPath, true);
719 }
720 }
721
722 public long getLastModified() {
723 return lastModified;
724 }
725
726 public String getETag() {
727 return etag;
728 }
729
730 public String getHash() {
731 return hash;
732 }
733
734 public byte[] getRawBuffer() {
735 return sourceBuffer;
736 }
737
738 public byte[] getGZipedBuffer() {
739 return sourceBufferGZip;
740 }
741 }
742
743 protected class SourceFile {
744 private String fileName;
745
746 public String getFileName() {
747 return fileName;
748 }
749
750 public void setFileName(String fileName) {
751 this.fileName = fileName;
752 }
753
754 public String toString() {
755 return "[SourceFile fileName='" + fileName + "']";
756 }
757 }
758 }