RE: Publication date
"Marc Cardle" <[email protected]>
| Newsgroups | gmane.comp.cms.jahia.template |
|---|---|
| Message-ID | <[email protected]> |
Ghislain, Glad it worked for you. Porting all your header code to OperationManager could indeed be worthwhile. If you do decide to go ahead, feel free to post your final code to this mailing so other Jahia users can benefit from it. Regards, Marc _____ From: ghislain.cussonneau [mailto:[email protected]] Sent: 14 December 2005 18:23 To: template_list Subject: RE: Publication date Ok, I've test with JMeter my server (100 concurents users load in 10 seconds and read 10 times a list of pages on the site) with and without this personal OperationManager and after charging both Jahia and Apache cache (1 user load 2 times all the list of pages). The results are the following : With Personal OperationManager : Average=500ms, Median=125ms, 90%=1400, Min=0, Max=20000, Errors=0, Flow=6.0/sec, KB/sec=350 Without Personal OperationManager : Average=600ms, Median=150ms, 90%=1700, Min=0, Max=15000, Errors=0, Flow=5.7/sec, KB/sec=300 So I would use your adding code : Thanks ! I think so about that : I would try to add my JSP/Template Header send code in the OperationManager's one. In this way, all my sites would have the "send cache header" support ! It would very better... Just a correction on your code : the correct one for integration with my HtmlCacheEntry class is : //--------------------- BEGIN: NEW CODE --------------------------- //return an HTTP 304 Not modified if cache entry hasn't been changed since last request HttpServletRequest realReq = jParams.getRealRequest(); long modSinceHeader = realReq.getDateHeader("If-Modified-Since"); if (modSinceHeader !=-1) { Date modSinceHeaderDate = new Date(modSinceHeader); Date cacheLastMod = new Date(); cacheLastMod.setTime(htmlEntry.getContentTimestamp()); if ( cacheLastMod.before(modSinceHeaderDate)) { realResp.setStatus(304); return; } } //--------------------- END: NEW CODE --------------------------- REGARDS Ghislain _____ Ghislain, In order to apply my aforementioned modifications to the Jahia 5.0s org.jahia.operations.valves.CacheReadValve class to Jahia versions 4.0 or 4.1, you need to modify the org.jahia.operations.OperationManager.handleOperations() method (which functionally overlaps): if (jParams.settings().lookupBoolean(SettingsBean.OUTPUT_CACHE_ACTIVATED)) { /** we only do HTML content caching if it is a core engine call * and if the HTTP method is GET. We might extend on this later * on but for the sake of simplicity of implementation we * restrict it for now. We might be interested in doing more * work here in order to cache more often. */ if ( ("core".equals(jParams.getEngine())) && (jParams.getHttpMethod() == ParamBean.GET_METHOD) && (ParamBean.CACHE_ON.equals(jParams.getCacheStatus())) ) { // logger.debug("Requested page is : " + jParams.getPageID() ); String entryKey = htmlCache.computeEntryKey( Integer.toString (jParams.getPageID ()), jParams.getUser().getUsername(), curLanguageCode, workflowState, jParams.getUserAgent ()); CacheEntry cacheEntry = htmlCache.getCacheEntry(entryKey); if (cacheEntry != null) { // logger.debug ("Found HTML page in cache!!!!!!!!!!!!!!!!!!"); HtmlCacheEntry htmlEntry = (HtmlCacheEntry)cacheEntry.getObject(); String htmlContent = htmlEntry.getContentBody(); if (!cacheEntry.getOperationMode().equals(jParams.getOperationMode())) { logger.debug("Cache entry mode is NOT equal to current mode, flushing page entry and generating page..."); htmlCache.remove (entryKey); } else { if (htmlContent != null) { logger.debug("Found content in cache, writing directly bypassing processing..."); HttpServletResponse realResp = jParams.getRealResponse(); //--------------------- BEGIN: NEW CODE --------------------------- //return an HTTP 304 Not modified if cache entry hasn't been changed since last request HttpServletRequest realReq = jParams.getRealRequest(); long modSinceHeader = realReq.getDateHeader("If-Modified-Since"); if (modSinceHeader !=-1) { Date modSinceHeaderDate = new Date(modSinceHeader); Date cacheLastMod = htmlEntry.getContentTimestamp(); if ( cacheLastMod.before(modSinceHeaderDate)) { realResp.setStatus(304); return; } } //--------------------- END: NEW CODE --------------------------- String contentType = htmlEntry.getContentType(); if (contentType != null) { realResp.setContentType(contentType); logger.debug("Sending content type : [" + contentType + "]"); } try { ServletOutputStream outputStream = realResp.getOutputStream(); OutputStreamWriter streamWriter = new OutputStreamWriter(outputStream); if (contentType != null) { int charsetPos = contentType.toLowerCase().indexOf("charset="); if (charsetPos != -1) { String encoding = contentType.substring(charsetPos + "charset=".length()).toUpperCase(); logger.debug("Using streamWriter with encoding : " + encoding); streamWriter = new OutputStreamWriter (outputStream, encoding); } } Regards Marc > _____ From: Marc Cardle [mailto:[email protected]] Sent: 13 December 2005 11:28 To: [email protected] Subject: RE: Publication date Ghislain, Thanks for your contribution! This code will be particularly useful for people running Jahia 4.0 and 4.1 (with or without a front-end Apache server). In Jahia 5.0, we are developing an ESI-enabled webcache server which supports more advanced caching features. It will replace, and be more efficient, than a front-end Apache server. In the meantime, why not go even further with your code and use the If-Not-Modified HTTP header? If Jahia detects such a header in the request and the cache entry is old enough, it will quit processing the page and return an HTTP 304 Not-Modified header ( http://www.checkupdown.com/status/E304.html ). The best place to add this functionality would be in the org.jahia.operations.valves.CacheReadValve class. I have written a (untested) mockup of the necessary changes below which you are welcome to try. Check for the BEGIN: NEW CODE tagged section. Again, youd need to recompile the CacheReadValve class and place it in the appropriate package directory in your webapps/jahia/WEB-INF/classes directory. public void invoke (Object context, ValveContext valveContext) throws PipelineException { ProcessingContext processingContext = (ProcessingContext) context; // first order of business is to do all cache processing, in order to // minimize what we have to do. if (processingContext.settings().lookupBoolean(SettingsBean. OUTPUT_CACHE_ACTIVATED)) { /** we only do HTML content caching if it is a core engine call * and if the HTTP method is GET. We might extend on this later * on but for the sake of simplicity of implementation we * restrict it for now. We might be interested in doing more * work here in order to cache more often. */ HtmlCache htmlCache = null; try { // Get the HTML cache instance htmlCache = ServicesRegistry.getInstance().getCacheService().getHtmlCacheInstance(); } catch (JahiaInitializationException ex) { throw new PipelineException(ex); } // extract the workflow state out of the parameters bean int workflowState = processingContext.getEntryLoadRequest(). getWorkflowState(); // Get the language code String curLanguageCode = LanguageCodeConverters.localeToLanguageTag( processingContext.getLocale()); if ( ("core".equals(processingContext.getEngine())) && (processingContext.getHttpMethod() == ProcessingContext.GET_METHOD) && (ProcessingContext.CACHE_ON.equals(processingContext.getCacheStatus())) ) { // logger.debug("Requested page is : " + jParams.getObjectKey() ); GroupCacheKey entryKey = htmlCache.computeEntryKey( Integer.toString(processingContext.getPageID()), processingContext.getUser().getUsername(), curLanguageCode, workflowState, processingContext.getUserAgent()); CacheEntry cacheEntry = htmlCache.getCacheEntry(entryKey); if (cacheEntry != null) { // logger.debug ("Found HTML page in cache!!!!!!!!!!!!!!!!!!"); HtmlCacheEntry htmlEntry = (HtmlCacheEntry) cacheEntry. getObject(); String htmlContent = htmlEntry.getContentBody(); if (!cacheEntry.getOperationMode().equals( processingContext.getOperationMode())) { logger.debug("Cache entry mode is NOT equal to current mode, flushing page entry and generating page..."); htmlCache.remove(entryKey); } else { if (htmlContent != null) { logger.debug( "Found content in cache, writing directly bypassing processing..."); HttpServletResponse realResp = ((ParamBean)processingContext). getRealResponse(); String contentType = htmlEntry.getContentType(); //--------------------- BEGIN: NEW CODE --------------------------- //return an HTTP 304 Not modified if cache entry hasn't been changed since last request HttpServletRequest realReq = ((ParamBean)processingContext). getRealRequest(); long modSinceHeader = realReq.getDateHeader("If-Modified-Since"); if (modSinceHeader !=-1) { Date modSinceHeaderDate = new Date(modSinceHeader); Date cacheLastMod = htmlEntry.getContentTimestamp(); if ( cacheLastMod.before(modSinceHeaderDate)) { realResp.setStatus(304); return; } } //--------------------- END: NEW CODE --------------------------- if (contentType != null) { realResp.setContentType(contentType); logger.debug("Sending content type : [" + contentType + "]"); } try { ServletOutputStream outputStream = realResp. getOutputStream(); OutputStreamWriter streamWriter = new OutputStreamWriter(outputStream); if (contentType != null) { int charsetPos = contentType.toLowerCase(). indexOf("charset="); if (charsetPos != -1) { String encoding = contentType.substring( charsetPos + "charset=".length()). toUpperCase(); logger.debug( "Using streamWriter with encoding : " + encoding); streamWriter = new OutputStreamWriter(outputStream, encoding); } } streamWriter.write(htmlContent, 0, htmlContent.length()); streamWriter.flush(); } catch (java.io.IOException ioe) { logger.error( "Error writing cache output, IOException generated error", ioe); JahiaException outputException = new JahiaException( "OperationsManager.handleOperations", "Error writing cache content to writer", JahiaException.SECURITY_ERROR, JahiaException.ERROR_SEVERITY, ioe); throw new PipelineException(outputException); } return; // exit handling here ! } } } else { logger.debug( "!!!!!! Could not find HTML page in cache!!!!!!!!!!!!!!!!!!"); } } if ( (ProcessingContext.CACHE_OFFONCE.equals(processingContext.getCacheStatus())) || (ProcessingContext.CACHE_BYPASS.equals(processingContext.getCacheStatus())) || (ProcessingContext.CACHE_ONLYUPDATE.equals(processingContext.getCacheStatus( ))) ) { // if we have a mode that is only temporary, all the // urls will be generated to have the cache active // by default. processingContext.setCacheStatus(ProcessingContext.CACHE_ON); } } else { logger.debug("Output cache not activated."); } valveContext.invokeNext(context); } Kind Regards, Marc > _____ From: ghislain.cussonneau [mailto:[email protected]] Sent: 12 December 2005 15:53 To: template_list Subject: RE: Publication date > > Ok ! > > I've done that and everything goes well ! For your interest, the following codes are the solution I've use (in order templates adding code / patch on jahia / apache conf). > > I've put the new HtmlCacheEntry in webapps/jahia/WEB-INF/classes directory, modify the jahia.properties to add the property "readonlyserver" and restart the server. > > TEMPLATES > > Here is the code I had in header.inc (after the include of declarations.inc) : > // Definition des headers en fonction des droits des utilisateurs // Si on est en anonyme ou que le serveur est utilisé en lecture uniquement --> utilisation du cache Apache if (!jData.gui().isLogged() || bIsReadonlyServer) { int iCacheMaxExpire = 24*3600; // 24 heures max pour cache Apache // Get the language code String curLanguageCode = LanguageCodeConverters.localeToLanguageTag(jParams.getLocale()); // Extract the workflow state out of the parameters bean int workflowState = jParams.getEntryLoadRequest().getWorkflowState(); // Get the HTML cache instance HtmlCache htmlCache = CacheFactory.getHtmlCache(); // Get the CacheEntry String entryKey = htmlCache.computeEntryKey(Integer.toString(jParams.getPageID()), jParams.getUser().getUsername(), curLanguageCode, workflowState, jParams.getUserAgent()); Date dLastModifiedCacheDate = new Date(); try { CacheEntry cacheEntry = (CacheEntry)htmlCache.getCacheEntry(entryKey); HtmlCacheEntry htmlEntry = (HtmlCacheEntry) cacheEntry.getObject(); dLastModifiedCacheDate.setTime(htmlEntry.getContentTimestamp()); } catch(Exception e) {} Date dExpires = new Date(); dExpires.setTime(dLastModifiedCacheDate.getTime() + iCacheMaxExpire); logger.debug("Header Expires = " + getDateAsRFC822String(dExpires)); logger.debug("Header Last-Modified = " + getDateAsRFC822String(dLastModifiedCacheDate)); logger.debug("Header Cache-Control = " + "max-age=" + iCacheMaxExpire + ", must-revalidate"); response.addHeader("Expires", getDateAsRFC822String(dExpires)); response.addHeader("Last-Modified", getDateAsRFC822String(dLastModifiedCacheDate)); response.addHeader("Cache-Control", "max-age=" + iCacheMaxExpire + ", must-revalidate"); } // Autrement --> pas de cache Apache else { Date dExpiresAbsolute = new Date(); dExpiresAbsolute.setTime(dExpiresAbsolute.getTime() + 24*3600); logger.debug("Header Cache-Control = no-cache"); logger.debug("Header Pragma = No-Cache"); logger.debug("Header ExpiresAbsolute = " + getDateAsRFC822String(dExpiresAbsolute)); logger.debug("Header Expires = -1"); response.addHeader("Cache-Control", "no-cache"); response.addHeader("Pragma", "No-Cache"); response.addHeader("ExpiresAbsolute", getDateAsRFC822String(dExpiresAbsolute)); response.addHeader("Expires", "-1"); } %> > > Here is the code I had in declarations.inc : > > private static PropertiesManager properties = new PropertiesManager(Jahia.getJahiaPropertiesFileName ()); private boolean bIsReadonlyServer = "true".equals(properties.getProperty("readonlyserver")); > public static SimpleDateFormat RFC822DATEFORMAT = new SimpleDateFormat("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.US); public static String getDateAsRFC822String(Date date) { return RFC822DATEFORMAT.format(date); } > > > PATCH JAHIA 4.0.6 > > Here is my personnal org.jahia.services.cache.HtmlCacheEntry (in red the code I've had) : > > > public class HtmlCacheEntry implements Serializable { > > /** the HTML body content. */ private String contentBody = ""; > > /** the HTML content type. */ private String contentType = ""; > > /** the date of HTMLCacheEntry creation / update **/ private long contentTimestamp = -1; > public HtmlCacheEntry (String contentBody) { init (contentBody, null, new Date().getTime()); } > public HtmlCacheEntry (String contentBody, String contentType) { init (contentBody, contentType, new Date().getTime()); } > > private void init (String contentBody, String contentType, long contentTimestamp) { if (contentBody != null) this.contentBody = contentBody; > if (contentType != null) this.contentType = contentType; this.contentTimestamp = contentTimestamp; } > final public String getContentType() { return contentType; } > final public void setContentType (String contentType) { this.contentType = contentType; this.contentTimestamp = new Date().getTime(); } > final public String getContentBody() { return contentBody; } > final public void setContentBody (String contentBody) { this.contentBody = contentBody; this.contentTimestamp = new Date().getTime(); } > final public long getContentTimestamp() { return contentTimestamp; } > } > Then in Apache, I've had a new virtual site declaration for my site > > > ServerName www.mysite.mydomain.fr <http://www.mysite.mydomain.fr/> > # Logging access CustomLog "|/apache/2.0.54/bin/rotatelogs /apache/logs/jahiaconsult_access_mysitekey_log.%Y-%m-%d-%H_%M_%S.log 86400" common > # Redirections Redirect /index.html http://www.mysite.mydomain.fr/jahia/Jahia/site/mysitekey DocumentRoot /apache/2.0.54/htdocs > RewriteEngine On > RewriteRule /favicon.ico - [F] RewriteRule (.*)/op(.*)$ http://www.mysite.mydomain.edit.fr/jahia/Jahia/engineName/login/site/mysitek ey RewriteRule ^/jahia/administration(.*)$ http://www.mysite.mydomain.edit.fr/jahia/administration > ProxyPass /jahia/Jahia/op ! ProxyPass /jahia/Jahia/site/mysitekey/op ! ProxyPass /jahia/administration ! ProxyPass /jahia http://www.mysite.mydomain.fr:9080/jahia ProxyPassReverse /jahia http://www.mysite.mydomain.fr:9080/jahia > # Gestion du Cache CacheRoot "/apache/2.0.54/proxy/www.mysite.mydomain.fr" CacheSize 50000 CacheEnable disk / CacheGcInterval 0.25 CacheMaxExpire 180 CacheLastModifiedFactor 0.1 CacheDefaultExpire 0.25 CacheDirLength 2 CacheDirLevels 3 > # Expire - by request ExpiresActive On ExpiresByType image/gif A86400 ExpiresByType image/png A86400 ExpiresByType image/jpeg A86400 ExpiresByType text/css A86400 > > > Regards for your Help. > > > Ghislain CUSSONNEAU > DIRR/DPIL/CIS > CAP 44, Rue Marcel Sembat > 44000 Nantes > 02 51 84 48 80 > > > > Accédez au courrier électronique de La Poste : www.laposte.net ; > Jusqu'au 25 décembre, participez au grand jeu du Calendrier de l'Avent et > gagnez tous les jours de nombreux lots, + de 300 cadeaux en jeu ! Accédez au courrier électronique de La Poste : www.laposte.net ; Jusqu'au 25 décembre, participez au grand jeu du Calendrier de l'Avent et gagnez tous les jours de nombreux lots, + de 300 cadeaux en jeu !