View Javadoc

1   /*
2    * $Id: SourceContainer.java,v 1.3 2010/03/19 14:53:48 oeuillot Exp $
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   * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
44   * @version $Revision: 1.3 $ $Date: 2010/03/19 14:53:48 $
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      // private byte[] sourceBufferExternalGZip = null;
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                             // Probleme ? la fermeture ?
170                         }
171 
172                         return url;
173                     }
174                 } catch (IOException ex) {
175                     // Rien ...
176                     if (LOG.isDebugEnabled()) {
177                         LOG.debug("IOException for servletContext resource '"
178                                 + url + "'", ex);
179                     }
180                 }
181             }
182 
183         } catch (MalformedURLException e) {
184             // Rien ...
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                         // Probleme ? la fermeture ?
215                     }
216 
217                     return url;
218                 }
219             } catch (IOException ex) {
220                 // Rien ...
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      * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
450      * @version $Revision: 1.3 $ $Date: 2010/03/19 14:53:48 $
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      * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
472      * @version $Revision: 1.3 $ $Date: 2010/03/19 14:53:48 $
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 }