View Javadoc

1   /*
2    * $Id: ContentStorageEngineImpl.java,v 1.16 2010/08/10 14:02:30 oeuillot Exp $
3    */
4   package org.rcfaces.core.internal.contentStorage;
5   
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.io.Serializable;
9   
10  import javax.faces.FacesException;
11  import javax.faces.component.StateHolder;
12  import javax.faces.component.UIComponentBase;
13  import javax.faces.context.FacesContext;
14  
15  import org.apache.commons.logging.Log;
16  import org.apache.commons.logging.LogFactory;
17  import org.rcfaces.core.internal.Constants;
18  import org.rcfaces.core.internal.RcfacesContext;
19  import org.rcfaces.core.internal.adapter.IAdapterManager;
20  import org.rcfaces.core.internal.content.ContentAdapterFactory;
21  import org.rcfaces.core.internal.contentAccessor.BasicContentAccessor;
22  import org.rcfaces.core.internal.contentAccessor.BasicGeneratedResourceInformation;
23  import org.rcfaces.core.internal.contentAccessor.BasicGenerationResourceInformation;
24  import org.rcfaces.core.internal.contentAccessor.ContentAccessorFactory;
25  import org.rcfaces.core.internal.contentAccessor.IContentAccessor;
26  import org.rcfaces.core.internal.contentAccessor.IContentPath;
27  import org.rcfaces.core.internal.contentAccessor.IGeneratedResourceInformation;
28  import org.rcfaces.core.internal.contentAccessor.IGenerationResourceInformation;
29  import org.rcfaces.core.internal.lang.LimitedMap;
30  import org.rcfaces.core.internal.lang.StringAppender;
31  import org.rcfaces.core.lang.IAdaptable;
32  import org.rcfaces.core.lang.IContentFamily;
33  import org.rcfaces.core.model.IContentModel;
34  import org.rcfaces.core.provider.AbstractProvider;
35  
36  /**
37   * 
38   * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
39   * @version $Revision: 1.16 $ $Date: 2010/08/10 14:02:30 $
40   */
41  public class ContentStorageEngineImpl extends AbstractProvider implements
42          IContentStorageEngine {
43      private static final String REVISION = "$Revision: 1.16 $";
44  
45      private static final Log LOG = LogFactory
46              .getLog(ContentStorageEngineImpl.class);
47  
48      private static final String DISABLE_CACHE_PARAMETER = "org.rcfaces.core.contentStorage.DISABLE_CACHE";
49  
50      private final IContentStorageRepository contentStorageRepository = new BasicContentStorageRepository();
51  
52      private int contentStorageServletPathType;
53  
54      private final Object contentStorageServletURL_LOCK = new Object();
55  
56      private volatile String contentStorageServletURL;
57  
58      private IAdapterManager adapterManager;
59  
60      private boolean disableCache = false;
61  
62      private final LimitedMap registredContentsByGenerationInformation = new LimitedMap(
63              Constants.CONTENT_STORAGE_CACHE_SIZE);
64  
65      public void startup(FacesContext facesContext) {
66          super.startup(facesContext);
67  
68          RcfacesContext rcfacesContext = RcfacesContext
69                  .getInstance(facesContext);
70  
71          if (rcfacesContext.getContentStorageEngine() == null) {
72              rcfacesContext.setContentStorageEngine(this);
73          }
74  
75          disableCache = "true"
76                  .equalsIgnoreCase(facesContext.getExternalContext()
77                          .getInitParameter(DISABLE_CACHE_PARAMETER));
78          if (disableCache) {
79              LOG.info("Content storage cache is disabled. (Parameter '"
80                      + DISABLE_CACHE_PARAMETER + "' is setted to true.)");
81          }
82  
83          contentStorageServletPathType = IContentPath.CONTEXT_PATH_TYPE;
84  
85          adapterManager = rcfacesContext.getAdapterManager();
86      }
87  
88      public IContentStorageRepository getRepository() {
89          return contentStorageRepository;
90      }
91  
92      public String getId() {
93          return "ContentStorageEngine";
94      }
95  
96      public IContentAccessor registerContentModel(FacesContext facesContext,
97              IContentModel contentModel,
98              IGeneratedResourceInformation generatedInformation,
99              IGenerationResourceInformation generationInformation) {
100 
101         boolean modified = false;
102         if (generatedInformation == null) {
103             generatedInformation = new BasicGeneratedResourceInformation();
104         }
105 
106         if (generationInformation == null) {
107             generationInformation = new BasicGenerationResourceInformation();
108 
109             ((BasicGenerationResourceInformation) generationInformation)
110                     .setProcessAtRequest(true);
111         }
112 
113         if (contentStorageServletURL == null) {
114             synchronized (contentStorageServletURL_LOCK) {
115                 // Il faut desynchroniser le startup des servlets !
116                 if (contentStorageServletURL == null) {
117                     contentStorageServletURL = ContentStorageServlet
118                             .getContentStorageBaseURI(facesContext
119                                     .getExternalContext().getApplicationMap());
120 
121                     if (contentStorageServletURL == null) {
122                         LOG
123                                 .info("Content storage engine is disabled. (No started Content Storage Servlet)");
124 
125                         return ContentAccessorFactory.UNSUPPORTED_CONTENT_ACCESSOR;
126                     }
127                 }
128             }
129         }
130 
131         if (generatedInformation.getContentFamily() == null) {
132             generatedInformation.setContentFamily(IContentFamily.USER);
133         }
134 
135         // Content content = null;
136         boolean contentEngineMustBeRegistred = true;
137 
138         IContentStorageRepository repository = getRepository();
139 
140         String contentEngineId = null;
141         if (disableCache == false) {
142             contentEngineId = contentModel.getContentEngineId();
143             if (contentEngineId == null) {
144                 Content content = (Content) registredContentsByGenerationInformation
145                         .get(generationInformation);
146                 if (content != null) {
147                     contentEngineId = content.getContentEngineId();
148                     contentEngineMustBeRegistred = false;
149 
150                     if (LOG.isDebugEnabled()) {
151                         LOG.debug("Content is already in cache ! information='"
152                                 + generationInformation + "'");
153                     }
154 
155                     IGeneratedResourceInformation contentGeneratedResourceInformation = content
156                             .getGeneratedInformation();
157 
158                     if (contentGeneratedResourceInformation != null) {
159                         contentGeneratedResourceInformation
160                                 .copyTo(generatedInformation);
161                     }
162                 } else {
163                     if (LOG.isDebugEnabled()) {
164                         LOG.debug("Cache does not contain information='"
165                                 + generationInformation + "'");
166                     }
167 
168                 }
169             }
170         } else {
171             if (LOG.isDebugEnabled()) {
172                 LOG.debug("Cache is disabled for information='"
173                         + generationInformation + "'");
174             }
175         }
176 
177         generatedInformation.setProcessingAtRequest(generationInformation
178                 .isProcessAtRequest());
179         contentModel.setInformations(generationInformation,
180                 generatedInformation);
181 
182         IResolvedContent resolvedContent = null;
183 
184         if (contentEngineId != null) {
185             contentModel.setContentEngineId(contentEngineId);
186 
187             if (contentModel.checkNotModified()) { // Le modele doit verifier la
188                 // synchro entre le contentEngineId et le generationInformation
189                 if (LOG.isDebugEnabled()) {
190                     LOG.debug("ContentModel '" + contentModel
191                             + "' is not modified !");
192                 }
193                 resolvedContent = repository.load(contentEngineId);
194 
195                 if (resolvedContent == null) {
196                     // Le cache ne contient plus la ressource ... trop tard !
197                     contentEngineId = null;
198                     contentEngineMustBeRegistred = true;
199 
200                     registredContentsByGenerationInformation
201                             .remove(generationInformation);
202                 }
203             } else {
204                 // Modifié !
205 
206                 contentEngineId = null;
207                 contentEngineMustBeRegistred = true;
208 
209                 registredContentsByGenerationInformation
210                         .remove(generationInformation);
211             }
212         }
213 
214         if (resolvedContent == null) {
215 
216             if (generatedInformation.isProcessingAtRequest()) {
217                 // On verra plus tard (dans la servlet)
218 
219                 String specifiedSuffix = generationInformation
220                         .getResponseSuffix();
221                 String specifiedContentType = generationInformation
222                         .getResponseMimeType();
223 
224                 String specifiedResourceKey = null;
225                 if (generationInformation
226                         .getComputeResourceKeyFromGenerationInformation()) {
227                     specifiedResourceKey = BasicGenerationResourceInformation
228                             .generateResourceKeyFromGenerationInformation(generationInformation);
229                 }
230 
231                 long specifiedLastModificationDate = generationInformation
232                         .getResponseLastModified();
233 
234                 resolvedContent = new ResolvedContentAtRequest(contentModel,
235                         specifiedContentType, specifiedSuffix,
236                         specifiedLastModificationDate, specifiedResourceKey,
237                         generationInformation);
238 
239             } else {
240                 Object wrappedData = contentModel.getWrappedData();
241 
242                 // Il nous faut un IResolvedContent !
243 
244                 if (wrappedData instanceof IResolvedContent) {
245                     resolvedContent = (IResolvedContent) wrappedData;
246 
247                 } else {
248 
249                     AdaptationParameters parametrizedAdaptation = new AdaptationParameters(
250                             contentModel, generationInformation,
251                             generatedInformation);
252 
253                     try {
254                         if (wrappedData instanceof IAdaptable) {
255                             resolvedContent = (IResolvedContent) ((IAdaptable) wrappedData)
256                                     .getAdapter(IResolvedContent.class,
257                                             parametrizedAdaptation);
258                         }
259 
260                         if (resolvedContent == null) {
261                             resolvedContent = (IResolvedContent) adapterManager
262                                     .getAdapter(wrappedData,
263                                             IResolvedContent.class,
264                                             parametrizedAdaptation);
265                         }
266                     } catch (Exception ex) {
267                         LOG.error(
268                                 "Can not adapt content while render phase. (information='"
269                                         + generationInformation + "')", ex);
270                     }
271                 }
272 
273                 if (resolvedContent == null) {
274                     throw new FacesException("Can not transform raw object '"
275                             + contentModel.getClass()
276                             + "' to IResolvedContentModel !");
277                 }
278 
279                 try {
280                     // On provoque le calcul du buffer, car c'est du processing
281                     // sur le rendu !
282                     resolvedContent.getLength();
283 
284                 } catch (Exception e) {
285                     LOG.error(
286                             "Can not resolve content while render phase. (information='"
287                                     + generationInformation + "')", e);
288 
289                     return null;
290                 }
291             }
292         }
293 
294         // Permet de remettre la ressource dans le cache
295         String url = repository.save(resolvedContent, contentModel);
296 
297         if (contentEngineMustBeRegistred) {
298             contentEngineId = contentModel.getContentEngineId();
299             if (contentEngineId != null) {
300                 registredContentsByGenerationInformation.put(
301                         generationInformation, new Content(
302                         // generationInformation,
303                                 generatedInformation, contentEngineId));
304             }
305         }
306 
307         IContentAccessor contentAccessor = new BasicContentAccessor(null,
308                 contentStorageServletURL + '/' + url, generatedInformation
309                         .getContentFamily());
310 
311         contentAccessor.setPathType(contentStorageServletPathType);
312 
313         return contentAccessor;
314     }
315 
316     public IContentAccessor registerRaw(FacesContext facesContext, Object ref,
317             IGeneratedResourceInformation generatedInformation) {
318 
319         if (contentStorageServletURL == null) {
320             LOG
321                     .info("ContentStorage is not initialized. (Servlet path is invalid)");
322 
323             return ContentAccessorFactory.UNSUPPORTED_CONTENT_ACCESSOR;
324         }
325 
326         IResolvedContent resolvedContent = null;
327         if (ref instanceof IAdaptable) {
328             resolvedContent = (IResolvedContent) ((IAdaptable) ref).getAdapter(
329                     IResolvedContent.class, null);
330         }
331 
332         if (resolvedContent == null) {
333             resolvedContent = (IResolvedContent) adapterManager.getAdapter(ref,
334                     IResolvedContent.class, null);
335         }
336 
337         if (resolvedContent == null) {
338             throw new FacesException("Can not transform raw object '"
339                     + ref.getClass() + "' to IResolvedContent !");
340         }
341 
342         String url = getRepository().save(resolvedContent, null);
343 
344         if (generatedInformation.getContentFamily() == null) {
345             generatedInformation.setContentFamily(IContentFamily.USER);
346         }
347 
348         IContentAccessor contentAccessor = new BasicContentAccessor(
349                 facesContext, contentStorageServletURL + '/' + url,
350                 generatedInformation.getContentFamily());
351 
352         contentAccessor.setPathType(contentStorageServletPathType);
353 
354         return contentAccessor;
355     }
356 
357     /**
358      * 
359      * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
360      * @version $Revision: 1.16 $ $Date: 2010/08/10 14:02:30 $
361      */
362     public static class ResolvedContentAtRequest extends
363             AbstractResolvedContent implements IResolvedContentWrapper,
364             Serializable {
365         private static final String REVISION = "$Revision: 1.16 $";
366 
367         private static final long serialVersionUID = -7807317078965658005L;
368 
369         private final IContentModel contentModel;
370 
371         private transient IResolvedContent resolvedContent;
372 
373         private transient boolean errorState;
374 
375         private final String specifiedContentType;
376 
377         private final String specifiedURLSuffix;
378 
379         private final long specifiedLastModificationDate;
380 
381         private final String specifiedResourceKey;
382 
383         private final IGenerationResourceInformation generationInformation;
384 
385         public ResolvedContentAtRequest(IContentModel contentModel,
386                 String specifiedContentType, String specifiedURLSuffix,
387                 long specifiedLastModificationDate,
388                 String specifiedResourceKey,
389                 IGenerationResourceInformation generationInformation) {
390             this.contentModel = contentModel;
391 
392             if (specifiedURLSuffix == null && specifiedContentType != null) {
393                 specifiedURLSuffix = ContentAdapterFactory
394                         .getSuffixByMimeType(specifiedContentType);
395             }
396 
397             this.specifiedURLSuffix = specifiedURLSuffix;
398             this.specifiedContentType = specifiedContentType;
399             this.specifiedLastModificationDate = specifiedLastModificationDate;
400             this.specifiedResourceKey = specifiedResourceKey;
401             this.generationInformation = generationInformation;
402         }
403 
404         public String getContentType() {
405             // Le contentType peut changer ...
406             if (isContentResolved()) {
407                 return getResolvedContent().getContentType();
408             }
409 
410             return specifiedContentType;
411         }
412 
413         protected boolean isContentResolved() {
414             if (errorState) {
415                 return false;
416             }
417 
418             return resolvedContent != null;
419         }
420 
421         public String getURLSuffix() {
422             if (isContentResolved()) {
423                 return getResolvedContent().getURLSuffix();
424             }
425 
426             return specifiedURLSuffix;
427         }
428 
429         public InputStream getInputStream() throws IOException {
430             return getResolvedContent().getInputStream();
431         }
432 
433         public long getModificationDate() {
434             if (isContentResolved()) {
435                 return getResolvedContent().getModificationDate();
436             }
437 
438             return specifiedLastModificationDate;
439         }
440 
441         public int getLength() {
442             return getResolvedContent().getLength();
443         }
444 
445         public void appendHashInformations(StringAppender sa) {
446             // Il faut les infos du content model ???
447 
448             if (specifiedResourceKey != null) {
449                 // Pas besoin d'informations complementaires
450                 return;
451             }
452 
453             getResolvedContent().appendHashInformations(sa);
454         }
455 
456         public synchronized IResolvedContent getResolvedContent() {
457             if (errorState) {
458                 return null;
459             }
460 
461             if (resolvedContent != null) {
462                 return resolvedContent;
463             }
464 
465             try {
466                 Object wrappedData = contentModel.getWrappedData();
467 
468                 AdaptationParameters parametrizedAdaptation = new AdaptationParameters(
469                         contentModel, generationInformation, null);
470 
471                 if (wrappedData instanceof IAdaptable) {
472                     resolvedContent = (IResolvedContent) ((IAdaptable) wrappedData)
473                             .getAdapter(IResolvedContent.class,
474                                     parametrizedAdaptation);
475 
476                     if (resolvedContent != null) {
477                         return resolvedContent;
478                     }
479                 }
480 
481                 RcfacesContext rcfacesContext = RcfacesContext
482                         .getCurrentInstance();
483 
484                 resolvedContent = (IResolvedContent) rcfacesContext
485                         .getAdapterManager().getAdapter(wrappedData,
486                                 IResolvedContent.class, parametrizedAdaptation);
487 
488                 if (resolvedContent != null) {
489                     return resolvedContent;
490                 }
491 
492                 throw new FacesException(
493                         "Can not transform wrappedData of content model '"
494                                 + wrappedData + "' !");
495 
496             } catch (RuntimeException ex) {
497 
498                 errorState = true;
499 
500                 throw ex;
501             }
502         }
503 
504         public boolean isProcessAtRequest() {
505             return true;
506         }
507 
508         public boolean isErrored() {
509             IResolvedContent resolvedContent = getResolvedContent();
510 
511             return errorState || resolvedContent.isErrored();
512         }
513 
514         public String getETag() {
515             return getResolvedContent().getETag();
516         }
517 
518         public String getHash() {
519             return getResolvedContent().getHash();
520         }
521 
522         public String getResourceKey() {
523             if (isContentResolved() == false && isProcessAtRequest()) {
524                 // C'est traité sur la requete !
525 
526                 if (specifiedResourceKey != null) {
527                     return specifiedResourceKey;
528                 }
529 
530                 if (contentModel instanceof IResourceKey) {
531                     // L'objet peut tout de même savoir sa clef ???
532                     return ((IResourceKey) contentModel).getResourceKey();
533                 }
534                 return null;
535             }
536 
537             return getResolvedContent().getResourceKey();
538         }
539     }
540 
541     /**
542      * 
543      * @author Olivier Oeuillot (latest modification by $Author: oeuillot $)
544      * @version $Revision: 1.16 $ $Date: 2010/08/10 14:02:30 $
545      */
546     public static class Content implements StateHolder {
547         private static final String REVISION = "$Revision: 1.16 $";
548 
549         // private IGenerationResourceInformation generationInformation;
550 
551         private IGeneratedResourceInformation generatedInformation;
552 
553         private String contentEngineId;
554 
555         private boolean transientFlag;
556 
557         public Content(
558                 // IGenerationResourceInformation generationInformation,
559                 IGeneratedResourceInformation generatedInformation,
560                 String contentEngineId) {
561             this.contentEngineId = contentEngineId;
562             // this.generationInformation = generationInformation;
563             this.generatedInformation = generatedInformation;
564         }
565 
566         public boolean isTransient() {
567             return transientFlag;
568         }
569 
570         public void setTransient(boolean transientFlag) {
571             this.transientFlag = transientFlag;
572         }
573 
574         public void restoreState(FacesContext context, Object state) {
575             Object states[] = (Object[]) state;
576 
577             contentEngineId = (String) states[0];
578 
579             // generationInformation = (IGenerationResourceInformation)
580             // UIComponentBase.restoreAttachedState(context, states[1]);
581 
582             generatedInformation = (IGeneratedResourceInformation) UIComponentBase
583                     .restoreAttachedState(context, states[2]);
584         }
585 
586         public Object saveState(FacesContext context) {
587             Object states[] = new Object[3];
588 
589             states[0] = contentEngineId;
590 
591             // states[1] =
592             // UIComponentBase.saveAttachedState(context,generationInformation);
593 
594             states[2] = UIComponentBase.saveAttachedState(context,
595                     generatedInformation);
596 
597             return states;
598         }
599 
600         public final IGeneratedResourceInformation getGeneratedInformation() {
601             return generatedInformation;
602         }
603 
604         public final String getContentEngineId() {
605             return contentEngineId;
606         }
607     }
608 }