KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jahia > operations > OperationManager


1 //
2
// ____.
3
// __/\ ______| |__/\. _______
4
// __ .____| | \ | +----+ \
5
// _______| /--| | | - \ _ | : - \_________
6
// \\______: :---| : : | : | \________>
7
// |__\---\_____________:______: :____|____:_____\
8
// /_____|
9
//
10
// . . . i n j a h i a w e t r u s t . . .
11
//
12

13 // Here is the check to see if the page is Forbidden for the current user!
14
//
15

16 package org.jahia.operations;
17
18 import java.io.IOException JavaDoc;
19 import java.io.OutputStreamWriter JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Date JavaDoc;
22 import java.util.Hashtable JavaDoc;
23 import java.util.Locale JavaDoc;
24 import java.util.Map JavaDoc;
25
26 import javax.servlet.ServletContext JavaDoc;
27 import javax.servlet.ServletOutputStream JavaDoc;
28 import javax.servlet.http.HttpServletResponse JavaDoc;
29 import javax.servlet.http.HttpSession JavaDoc;
30
31 import org.apache.commons.collections.FastHashMap;
32 import org.apache.log4j.Logger;
33 import org.jahia.bin.Jahia;
34 import org.jahia.data.JahiaData;
35 import org.jahia.engines.JahiaEngine;
36 import org.jahia.exceptions.JahiaException;
37 import org.jahia.exceptions.JahiaForbiddenAccessException;
38 import org.jahia.exceptions.JahiaSessionExpirationException;
39 import org.jahia.exceptions.JahiaServerOverloadedException;
40 import org.jahia.params.ParamBean;
41 import org.jahia.registries.EnginesRegistry;
42 import org.jahia.services.cache.CacheEntry;
43 import org.jahia.services.cache.CacheFactory;
44 import org.jahia.services.cache.HtmlCache;
45 import org.jahia.services.cache.HtmlCacheEntry;
46 import org.jahia.services.database.ConnectionDispenser;
47 import org.jahia.settings.SettingsBean;
48 import org.jahia.utils.LanguageCodeConverters;
49
50 import EDU.oswego.cs.dl.util.concurrent.Latch;
51 import EDU.oswego.cs.dl.util.concurrent.Semaphore;
52 import EDU.oswego.cs.dl.util.concurrent.WaiterPreferenceSemaphore;
53
54 /**
55  * This class is responsible for dispatching to an engine. In the special case
56  * of the Core engine (output of CMS data), the output caching mechanism is
57  * interrogated to avoid processing a request and generate HTML if it isn't
58  * needed.
59  * <p>
60  * Title:
61  * </p>
62  * <p>
63  * Description:
64  * </p>
65  * <p>
66  * Copyright: Copyright (c) 2002
67  * </p>
68  * <p>
69  * Company: Jahia Inc.
70  * </p>
71  *
72  * @author Eric Vassalli
73  * @author Serge Huber
74  * @author Fulco Houkes
75  * @author Cédric Mailleux
76  * @version 3.1
77  */

78 public class OperationManager {
79
80     private static Logger logger = Logger.getLogger(OperationManager.class);
81
82     // Map containing the entry key of the currently generating page
83
private static Map JavaDoc generatingPage = new FastHashMap(10);
84
85     // Map containing the entry of the generated page
86
private static Map JavaDoc notCacheablePage = new FastHashMap(2503);
87
88     private PageState pageState = null;
89
90     private static final Semaphore availableProcessings = new WaiterPreferenceSemaphore(Jahia.getSettings().getMaxParallelProcessings());
91
92     /**
93      * Default constructor, creates a new <code>OperationManager</code> object
94      * instance
95      */

96     public OperationManager() {
97         logger.debug("Starting up...");
98     }
99
100     /**
101      * Handles the operations.
102      *
103      * @param jParams
104      * the parameter bean
105      * @param jSettings
106      * the Jahia settings
107      *
108      * @throws org.jahia.exceptions.JahiaException
109      * when a general exception occured
110      *
111      * @throws org.jahia.exceptions.JahiaSessionExpirationException
112      * when the user session expired
113      *
114      * @throws org.jahia.exceptions.JahiaForbiddenAccessException
115      * when the user has not access to the requested operation
116      */

117     public void handleOperations(ParamBean jParams, SettingsBean jSettings)
118             throws JahiaException, JahiaSessionExpirationException,
119             JahiaForbiddenAccessException {
120         // SiteCheck
121
if (handleSiteCheck(jParams))
122             return;
123
124         handleOperationModeCheck(jParams);
125
126         // Get the engine instance (can throw a JahiaException)
127
String JavaDoc engineName = jParams.getEngine();
128         JahiaEngine theEngine = getEngineInstance(engineName);
129         
130         // PAP: to ensure that waiting threads do not hold open connections
131
ConnectionDispenser.terminateConnection();
132         
133         if (theEngine != null) {
134             // logger.debug("Engine=" + engineName);
135
if (theEngine.needsJahiaData(jParams)) {
136                 // logger.debug("Engine need JahiaData");
137

138                 handlePageAccessCheck(jParams);
139
140                 if (checkCache(jParams, null))
141                     return;
142             }
143
144             // So we did not find the page in the cache
145
// check if the page is already generating or is generated
146
// force generation
147
Latch latch = null;
148
149             // boolean which indicate if we have to generate the page or we
150
// must
151
// wait for the cache to be generated
152
boolean mustBeWaited = false;
153
154             if (pageState != null && pageState.isCacheable
155                     && !notCacheablePage.containsKey(pageState.getKey())) {
156                 synchronized (generatingPage) {
157                     if (!generatingPage.containsKey(pageState.getKey())) {
158                         latch = new Latch();
159                         generatingPage.put(pageState.getKey(), latch);
160                     } else {
161                         latch = (Latch) generatingPage.get(pageState
162                             .getKey());
163                         mustBeWaited = true;
164                     }
165                 }
166                 try {
167                     if (mustBeWaited) {
168                         if (!latch.attempt(jParams.settings().getPageGenerationWaitTime())) {
169                             throw new JahiaServerOverloadedException();
170                         }
171                         if (checkCache(jParams, null))
172                             return;
173                     }
174                 } catch (InterruptedException JavaDoc ie) {
175                     logger.debug("The waiting thread has been interrupted :",
176                         ie);
177                 }
178             }
179             boolean semaphoreAcquired = false;
180             try {
181                 if (!availableProcessings.attempt(jParams.settings().getPageGenerationWaitTime())){
182                   throw new JahiaServerOverloadedException();
183                 } else {
184                     semaphoreAcquired = true;
185                 }
186                 processPage(jParams, theEngine);
187             } catch (InterruptedException JavaDoc ie) {
188                 logger.debug("The waiting thread has been interrupted :",
189                         ie);
190             } finally {
191                 if (semaphoreAcquired) {
192                     availableProcessings.release();
193                 }
194                 if (latch != null) {
195                     latch.release();
196                 }
197                 if (pageState != null && pageState.getKey() != null) {
198                     synchronized (generatingPage) {
199                         generatingPage.remove(pageState.getKey());
200                     }
201                 }
202             }
203         }
204     }
205
206     private void processPage(ParamBean jParams, JahiaEngine theEngine)
207             throws JahiaException {
208         try {
209             generatePage(jParams, theEngine);
210
211             setResponseOutput(jParams);
212
213             if (Jahia.getSettings().lookupBoolean(
214                 SettingsBean.OUTPUT_CACHE_ACTIVATED)) {
215                 storeInCache(jParams, pageState != null ? pageState.getKey()
216                         : null, jParams.getGeneratedOutput(), jParams
217                     .getContentType());
218             }
219         } catch (java.io.IOException JavaDoc ioe) {
220             logger
221                 .debug(
222                     "Error retrieving real response writer object or while retrieving generated output :",
223                     ioe);
224         }
225     }
226
227     private boolean handleSiteCheck(ParamBean jParams) throws JahiaException {
228         boolean exitProcessing = false;
229         if (jParams.getSite() == null) {
230             ServletContext JavaDoc context = jParams.getContext();
231             if (context != null) {
232                 try {
233                     jParams.getRequest().setAttribute("Jahia_ParamBean",
234                         jParams);
235                     context.getRequestDispatcher(
236                         "/jsp/jahia/errors/site_not_found.jsp").forward(
237                         jParams.getRequest(), jParams.getResponse());
238                 } catch (Throwable JavaDoc t) {
239                     logger.error(t);
240                     throw new JahiaException(
241                         "400 Page Not Found: No site specified",
242                         "The requested site not found",
243                         JahiaException.SECURITY_ERROR,
244                         JahiaException.ERROR_SEVERITY);
245                 }
246                 exitProcessing = true;
247             } else {
248                 throw new JahiaException("400 Page Not Found:",
249                     "Context is null", JahiaException.SECURITY_ERROR,
250                     JahiaException.ERROR_SEVERITY);
251             }
252         }
253         return exitProcessing;
254     }
255
256     private void handleOperationModeCheck(ParamBean jParams)
257             throws JahiaException {
258         if ("core".equals(jParams.getEngine())) {
259             if (jParams.getPage() == null
260                     && (jParams.getOperationMode().equals(ParamBean.EDIT) || jParams
261                         .getOperationMode().equals(ParamBean.COMPARE))) {
262                 // try to switch to NORMAL Mode
263
// force to recompute all locales be removing session locale
264
// and switching to NORMAL MODE
265
jParams.getSession().removeAttribute(ParamBean.SESSION_LOCALE);
266                 jParams.flushLocaleListCache();
267                 ArrayList JavaDoc locales = jParams.getLocales(true);
268                 Hashtable JavaDoc pageTitles = jParams.getContentPage().getTitles(true);
269                 Locale JavaDoc locale = null;
270                 String JavaDoc lang = null;
271                 for (int i = 0; i < locales.size(); i++) {
272                     locale = (Locale JavaDoc) locales.get(i);
273                     if (pageTitles.containsKey(locale.toString())) {
274                         lang = locale.toString();
275                         break;
276                     }
277                 }
278
279                 if (lang != null) {
280                     jParams.getSession().setAttribute(ParamBean.SESSION_LOCALE,
281                         locale);
282                     jParams.setUser(jParams.getUser());
283                     jParams.changePage(jParams.getContentPage());
284                 }
285             }
286         }
287     }
288
289     private void handlePageAccessCheck(ParamBean jParams) throws JahiaException {
290         // Check the rights for the current page. We do this here
291
// because it the
292
// engine doesn't need a JahiaData it means that it is not
293
// related to
294
// the current page, so we let the user display the engine!
295
// if no read access -> launch exception
296

297         if (jParams.getPage() != null
298                 && !jParams.getPage().checkReadAccess(jParams.getUser())) {
299             if (Jahia.usesSso()) {
300                 try {
301                     jParams.getResponse().sendRedirect(Jahia.getSsoValve().getRedirectUrl(jParams));
302                     return;
303                 } catch (IOException JavaDoc e) {
304                 }
305             }
306             throw new JahiaException("403 Forbidden - Page:"
307                     + jParams.getPageID(), "No read access for page "
308                     + jParams.getPageID() + " user="
309                     + jParams.getUser().getUsername() + " acl="
310                     + jParams.getPage().getAclID(),
311                 JahiaException.SECURITY_ERROR, JahiaException.ERROR_SEVERITY);
312         }
313     }
314
315     private void generatePage(ParamBean jParams, JahiaEngine theEngine)
316             throws JahiaException {
317         try {
318             JahiaData jData = null;
319
320             if (logger.isDebugEnabled()) {
321                 logger.debug("We generate page "
322                         + (pageState != null ? pageState.getKey() : ""));
323                 // logger.debug("Cache mode "+jParams.getCacheStatus()+"
324
// original cache "+jParams.getOriginalCacheStatus());
325
// logger.debug("Engine type "+theEngine.getName());
326
}
327
328             if (theEngine.needsJahiaData(jParams) && jParams.getPage() != null) {
329                 jData = new JahiaData(jParams);
330             } else {
331                 jData = new JahiaData(jParams, false);
332             }
333
334             // for JSp
335
jParams.getRequest()
336                 .setAttribute("org.jahia.data.JahiaData", jData);
337
338             logger.debug("Start of handling operation");
339             theEngine.handleActions(jParams, jData);
340             logger.debug("Operation handled for engine " + jParams.getEngine());
341
342             HttpSession JavaDoc session = jParams.getRequest().getSession();
343
344             // save last engine name in session
345
session.setAttribute(ParamBean.SESSION_LAST_ENGINE_NAME, jParams
346                 .getEngine());
347
348             // save last requested page id in session
349
session.setAttribute(ParamBean.SESSION_LAST_REQUESTED_PAGE_ID,
350                 new Integer JavaDoc(jParams.getPageID()));
351         } catch (Throwable JavaDoc e) {
352             throw new JahiaException("Error during handle of operation",
353                 "Error during handle of operation", JahiaException.PAGE_ERROR,
354                 JahiaException.ERROR_SEVERITY, e);
355         }
356     }
357
358     private void setResponseOutput(ParamBean jParams) throws IOException JavaDoc {
359         // Let's retrieve the generated content from the response wrapper
360
// object.
361
HttpServletResponse JavaDoc realResp = jParams.getRealResponse();
362         String JavaDoc curContentType = jParams.getContentType();
363         if (jParams.getRedirectLocation() != null) {
364             logger
365                 .debug("sendRedirect call detected during output generation, no other output...");
366             if (!realResp.isCommitted()) {
367                 realResp.sendRedirect(realResp.encodeRedirectURL(jParams
368                     .getRedirectLocation()));
369             }
370         } else {
371             /**
372              * @todo we should really find a more elegant way to handle this
373              * case, especially when handling a file manager download that
374              * has already accessed the RealResponse object.
375              */

376             if (!realResp.isCommitted()) {
377                 logger.debug("Printing content output to real writer");
378                 if (curContentType != null) {
379                     realResp.setContentType(curContentType);
380                 }
381                 // PrintWriter writer = realResp.getWriter();
382
// writer.print(generatedOutput);
383
/*
384                  * ServletOutputStream outputStream =
385                  * realResp.getOutputStream();
386                  * outputStream.print(generatedOutput);
387                  */

388             } else {
389                 logger
390                     .debug("Output has already been committed, aborting display...");
391             }
392         }
393     }
394
395     private boolean checkCache(ParamBean jParams, Object JavaDoc entryKey)
396             throws JahiaException {
397         boolean isCacheable = false;
398         boolean isBeingGenerated = false;
399
400         if (!Jahia.getSettings().lookupBoolean(
401             SettingsBean.OUTPUT_CACHE_ACTIVATED)) {
402             logger.debug("Output cache not activated.");
403         } else {
404             /**
405              * we only do HTML content caching if it is a core engine call and
406              * if the HTTP method is GET. We might extend on this later on but
407              * for the sake of simplicity of implementation we restrict it for
408              * now. We might be interested in doing more work here in order to
409              * cache more often.
410              */

411             if (("core".equals(jParams.getEngine()))
412                     && (jParams.getHttpMethod() == ParamBean.GET_METHOD)
413                     && (ParamBean.CACHE_ON.equals(jParams.getCacheStatus()))) {
414                 // Get the HTML cache instance
415
HtmlCache htmlCache = CacheFactory.getHtmlCache();
416
417                 if (entryKey == null) {
418                     // Get the language code
419
String JavaDoc curLanguageCode = LanguageCodeConverters
420                         .localeToLanguageTag(jParams.getLocale());
421                     // extract the workflow state out of the parameters bean
422
int workflowState = jParams.getEntryLoadRequest()
423                         .getWorkflowState();
424                     
425                     entryKey = htmlCache.computeEntryKey(Integer
426                         .toString(jParams.getPageID()), jParams.getUser()
427                         .getUsername(), curLanguageCode, workflowState, jParams
428                         .getUserAgent(), jParams.getRequest().getScheme());
429                 }
430
431                 // logger.debug("Requested page is : " + jParams.getPageID() );
432

433                 CacheEntry cacheEntry = htmlCache.getCacheEntry(entryKey);
434
435                 // This page is cacheable
436
if (jParams.getCacheExpirationDelay() == -1
437                         || jParams.getCacheExpirationDelay() > 0)
438                     isCacheable = true;
439
440                 if (cacheEntry != null) {
441                     // logger.debug ("Found HTML page in
442
// cache!!!!!!!!!!!!!!!!!!");
443
HtmlCacheEntry htmlEntry = (HtmlCacheEntry) cacheEntry
444                         .getObject();
445                     String JavaDoc htmlContent = htmlEntry.getContentBody();
446                     if (!cacheEntry.getOperationMode().equals(
447                         jParams.getOperationMode())) {
448                         logger
449                             .debug("Cache entry mode is NOT equal to current mode, flushing page entry and generating page...");
450                         htmlCache.remove(entryKey);
451                     } else if (htmlContent != null) {
452                         logger
453                             .debug("Found content in cache, writing directly bypassing processing...");
454                         HttpServletResponse JavaDoc realResp = jParams
455                             .getRealResponse();
456                         String JavaDoc contentType = htmlEntry.getContentType();
457                         if (contentType != null) {
458                             realResp.setContentType(contentType);
459                             logger.debug("Sending content type : ["
460                                     + contentType + "]");
461                         }
462                         try {
463                             ServletOutputStream JavaDoc outputStream = realResp
464                                 .getOutputStream();
465                             OutputStreamWriter JavaDoc streamWriter = new OutputStreamWriter JavaDoc(
466                                 outputStream);
467                             if (contentType != null) {
468                                 int charsetPos = contentType.toLowerCase()
469                                     .indexOf("charset=");
470                                 if (charsetPos != -1) {
471                                     String JavaDoc encoding = contentType.substring(
472                                         charsetPos + "charset=".length())
473                                         .toUpperCase();
474                                     logger
475                                         .debug("Using streamWriter with encoding : "
476                                                 + encoding);
477                                     streamWriter = new OutputStreamWriter JavaDoc(
478                                         outputStream, encoding);
479                                 }
480                             }
481                             streamWriter.write(htmlContent, 0, htmlContent
482                                 .length());
483                             streamWriter.flush();
484                         } catch (java.io.IOException JavaDoc ioe) {
485                             logger
486                                 .error(
487                                     "Error writing cache output, IOException generated error",
488                                     ioe);
489                             throw new JahiaException(
490                                 "OperationsManager.handleOperations",
491                                 "Error writing cache content to writer",
492                                 JahiaException.SECURITY_ERROR,
493                                 JahiaException.ERROR_SEVERITY, ioe);
494                         }
495                         return (true);
496                     }
497                 }
498             }
499
500             if ((ParamBean.CACHE_OFFONCE.equals(jParams.getCacheStatus()))
501                     || (ParamBean.CACHE_BYPASS.equals(jParams.getCacheStatus()))
502                     || (ParamBean.CACHE_ONLYUPDATE.equals(jParams
503                         .getCacheStatus()))) {
504                 // if we have a mode that is only temporary, all the
505
// urls will be generated to have the cache active
506
// by default.
507
jParams.setCacheStatus(ParamBean.CACHE_ON);
508             }
509         }
510
511         pageState = new PageState(isCacheable, isBeingGenerated, entryKey);
512
513         return (false);
514     }
515
516     /**
517      * Stores the generated HTML content into the specified HTML cache.
518      *
519      * @param jParams
520      * the parameter bean
521      * @param entryKey
522      * the cache entry key
523      * @param generatedOutput
524      * the html content body to be stored
525      * @param contentType
526      * the html content type
527      */

528     private void storeInCache(ParamBean jParams, Object JavaDoc entryKey,
529             String JavaDoc generatedOutput, String JavaDoc contentType) throws JahiaException {
530         logger.debug("Storing generated content into the HTML Cache...");
531
532         // Get the HTML cache instance
533
HtmlCache htmlCache = CacheFactory.getHtmlCache();
534
535         if (entryKey == null) {
536             // Get the language code
537
String JavaDoc curLanguageCode = LanguageCodeConverters
538                 .localeToLanguageTag(jParams.getLocale());
539             // extract the workflow state out of the parameters bean
540
int workflowState = jParams.getEntryLoadRequest()
541                 .getWorkflowState();
542             entryKey = htmlCache.computeEntryKey(Integer.toString(jParams
543                 .getPageID()), jParams.getUser().getUsername(),
544                 curLanguageCode, workflowState, jParams.getUserAgent(),
545                 jParams.getRequest().getScheme());
546         }
547
548         // Modified getOriginalCacheStatus to cacheStatus
549
if (("core".equals(jParams.getEngine()))
550                 && ((ParamBean.CACHE_ON
551                     .equals(jParams.getOriginalCacheStatus()))
552                         || (ParamBean.CACHE_ONLYUPDATE.equals(jParams
553                             .getOriginalCacheStatus())) || (ParamBean.CACHE_OFFONCE
554                     .equals(jParams.getOriginalCacheStatus())))) {
555             if (logger.isDebugEnabled()) {
556                 // let's update the cache...
557
logger
558                     .debug("Storing output in cache for entryKey=" + entryKey);
559             }
560             HtmlCacheEntry htmlEntry = new HtmlCacheEntry(generatedOutput,
561                 contentType);
562             CacheEntry newEntry = new CacheEntry(htmlEntry);
563             newEntry.setOperationMode(jParams.getOperationMode());
564
565             // Cannot proceed if the user information is not available
566
if (jParams.getUser() == null)
567                 return;
568
569             // Add the entry to the cache
570
if (jParams.getCacheExpirationDelay() == 0)
571                 notCacheablePage.put(entryKey, entryKey);
572             // compute the entry's expiration date
573
if (Jahia.getSettings().getOutputCacheDefaultExpirationDelay() != -1) {
574                 Date JavaDoc nowDate = new Date JavaDoc();
575
576                 // create the expiration date by adding an expiration timeout to
577
// the
578
// current system date.
579
Date JavaDoc expirationDate = new Date JavaDoc(nowDate.getTime()
580                         + Jahia.getSettings()
581                             .getOutputCacheDefaultExpirationDelay());
582                 logger.debug("Set the expiration date");
583                 newEntry.setExpirationDate(expirationDate);
584             }
585
586             // if a default expiration date has been set, us this instead.
587
if (jParams.getCacheExpirationDate() != null) {
588                 logger.debug("Using default expiration date");
589                 Date JavaDoc nowDate = new Date JavaDoc();
590                 if (nowDate.compareTo(jParams.getCacheExpirationDate()) >= 0) {
591                     // cache will expire immediately -> we don't cache the
592
// page.
593
return;
594                 }
595                 newEntry.setExpirationDate(jParams.getCacheExpirationDate());
596             }
597
598             htmlCache.putCacheEntry(entryKey, newEntry, true);
599             logger.debug("Added HTML page into the cache.");
600         } else {
601             logger.debug("Bypassing HTML cache storage");
602             if (htmlCache != null && jParams.getUser() != null) {
603                 if (jParams.getCacheExpirationDelay() == 0) {
604                     notCacheablePage.put(entryKey, entryKey);
605                 }
606             }
607         }
608     }
609
610     /**
611      * Retrieve the requested engine instance.
612      *
613      * @param name
614      * the engine name
615      *
616      * @return the reference to the engine, or <code>null</code> when the
617      * engine name is unknown in the Engines Registry.
618      *
619      * @throws JahiaException
620      * when the Engines Registry reference could not be retrieved.
621      */

622     private JahiaEngine getEngineInstance(String JavaDoc name) throws JahiaException {
623         // Get the Engines Registry
624
EnginesRegistry registry = EnginesRegistry.getInstance();
625         if (registry == null)
626             throw new JahiaException("Internal Error",
627                 "Could not get the Engines Registry instance!",
628                 JahiaException.INITIALIZATION_ERROR,
629                 JahiaException.ERROR_SEVERITY);
630
631         // Return the requested engine
632
return (JahiaEngine) registry.getEngine(name);
633     }
634 }
635
Popular Tags