here you go :)

Should be able to drop these into your project.

The Buffer.java is the component ( so somewhere under components package ), and the BufferServices needs to go somewhere where Tapestry will not enhance it, so I put it under the services package. ). :) Sorry, but the "cacheKey/Extra" stuff is a little confusing, but maybe we can start a different thread/docs for that, or simplify to make it easier for generalization..

The Parameters are:

1) timeout: String
  a duration in which a cache value will be valid; format: "hh:mm:ss"
  if you want a one hour timeout:  "01:00:00".
  if you want a 30 min timeout: "00:30:00"

2) lastUpdated: Date
  it output this parameter (if bound), to let you know when the contents
  of the Buffer were last updated.  So you can print out a small message
  "Last Updated ######"


3) cacheKey: String
4) cacehKeyExtra: String

here is where it might get a little confusing.. it generates a key
into the cache using the request server/port/context/path and cacheKey
and cacheKeyExtra.  So it focuses the cache onto the current server
and webapp ( since we used a shared memcache this avoids developers
and production webapps stepping on each others toes ).  It then
adds cacheKey (which defaults to the component's extendedId), and
cacheKeyExtra (which defaults to empty).

So by default just wrapping your stuff with <buffer timeout> will just work since the key will be something like "server:port/context_PageName:buffer_#".

You would set cacheKey if you wanted to generalize the contents of the buffer ( have the same Buffer component shared across pages ).

You would set cacheKeyExtra if you wanted to specialize the contents of the buffer ( have the same Buffer component apply to subsets of request.. like if it's user specific ).








Davor Hrg wrote:
I'm interested in the one for T5 :)

if you are not allowed to share source,
maybe few hints how to make it

Davor Hrg

On Tue, Mar 18, 2008 at 7:22 PM, Fernando Padilla <[EMAIL PROTECTED]> wrote:
We have a component that we call "Buffer" :) it takes a timeout,
 optional cachekey, and optional lastmodified (to tell you)

 We have it for Tap4 and Tap5, if anyone really wants it, I bet I can
 liberate it.. you would just have to change the cache hooks to use
 whatever cache you want to use...

 The easiest way to add caching to the app. :)



 Martin Kersten wrote:
 > @Chached is only used during a single page rendering cycle. It would not
 > apply to your situation. (as far as I know)
 >
 > Source:
 > http://sqllyw.wordpress.com/2008/03/15/new-features-in-tapestry-5011/
 >
 > Your scenario would be implementable using your own component.
 > The component would represent a fragment and read the file (even
 > use a inmemory cache strategy (soft/weakreferences)) and write
 > the ouput directly to stream (or actually the dom tree of your
 > document being returned).
 >
 > Using your own solution enables you to mimic the behavior you talk about.
 > Another idea would to write / cache only datas needed to render the tables
 > (e.g. cache only content not markup). Never the less I am in doupt,
 > if such a solution is necessary (dynamically cache results of
 > database queries in memory or on disk).
 >
 > So after all you might want to port your application. As always use
 > the simpliest solution first. So database queries without any caches.
 > Once you see any problems (performance is below required) just go for
 > optimization. Since you have a fallback solution at hand (cron-jobs +
 > disk fragments) you are at the safe side. But I am in doupt if you
 > really need the markup being cached. Caching the database results
 > and recreate markup sounds more reasonable. You might save you lots
 > of seeking time.
 >
 > But you always know: Only the code / application will tell you!
 >
 >
 > Cheers,
 >
 > Martin (Kersten)
 >
 > -----Ursprüngliche Nachricht-----
 > Von: Tobias Marx [mailto:[EMAIL PROTECTED]
 > Gesendet: Dienstag, 18. März 2008 17:45
 > An: Tapestry users
 > Betreff: @Cached and caching in general
 >
 > I have not used T5 yet, but would @Cached use the file system for caching 
HTML fragments similiar to caching mechanisms in some php frameworks?
 >
 > Or is this a pure memory-based cache?
 >
 > I am thinking about migrating an old PHP application to T5 - it has really a 
lot of traffic and any users are logged in at the same time.
 >
 > It is quite a low-level application that is still quite fast due to cron 
jobs in the background that generate HTML fragments that are included to reduce 
the database-query bottleneck (e.g. grouping/ordering and sorting of huge tables).
 >
 > Somehow I don't trust Hibernate for high-performance database queries on 
huge tables .... as I think if tables are huge and many people access it, it will 
always lead to problems...no matter how good the queries are and how well you have 
splitted the data across several tables.
 >
 > So I think the best solution is always to generate HTML fragments in the background 
that take a long time and simple "include" them....this is even quicker then 
parsing templates when the data is cached. So you save the time necessary for querying the 
database plus the time necessary for processing the templates that are involved.
 >
 > Currently the setup on this application uses one-way database replication 
and the cron jobs access the the huge data table on the replicated database and 
generate those HTML fragments without disturbing the web-applications performance. 
So the main application simply includes those HTML fragments within milliseconds.
 >
 > But maybe the T5 caching mechanism would make all of those low-level tricks 
redundant?
 >
 > ---------------------------------------------------------------------
 > To unsubscribe, e-mail: [EMAIL PROTECTED]
 > For additional commands, e-mail: [EMAIL PROTECTED]
 >
 >
 > ---------------------------------------------------------------------
 > To unsubscribe, e-mail: [EMAIL PROTECTED]
 > For additional commands, e-mail: [EMAIL PROTECTED]
 >

 ---------------------------------------------------------------------
 To unsubscribe, e-mail: [EMAIL PROTECTED]
 For additional commands, e-mail: [EMAIL PROTECTED]



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

package ...;

import java.io.CharArrayWriter;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.apache.tapestry.ComponentResources;
import org.apache.tapestry.MarkupWriter;
import org.apache.tapestry.PageRenderSupport;
import org.apache.tapestry.annotations.AfterRender;
import org.apache.tapestry.annotations.AfterRenderBody;
import org.apache.tapestry.annotations.BeforeRenderBody;
import org.apache.tapestry.annotations.BeginRender;
import org.apache.tapestry.annotations.Environmental;
import org.apache.tapestry.annotations.Parameter;
import org.apache.tapestry.annotations.Service;
import org.apache.tapestry.annotations.SetupRender;
import org.apache.tapestry.dom.Element;
import org.apache.tapestry.dom.Node;
import org.apache.tapestry.ioc.annotations.Inject;
import org.apache.tapestry.ioc.services.SymbolSource;
import org.apache.tapestry.services.AssetSource;
import org.apache.tapestry.services.Environment;
import org.apache.tapestry.services.RequestGlobals;

import ...BufferServices.BufferPageRenderSupport;
import ...BufferServices.BufferValue;
import ...BufferServices.CacheValue;

public class Buffer {

	protected static Logger logger = Logger.getLogger( Buffer.class );

	@Inject
	@Service("tc-cache-map-factory")
	private Map<String,Map<String,CacheValue>> mapFactory;
	private Map<String,CacheValue> cache;
	private String key;
	private CacheValue cv;

	@Inject
	private ComponentResources resources;

	public ComponentResources getResources() {
		return resources;
	}

	@Inject
	private RequestGlobals requestGlobals;

	@Inject
	private Environment environment;

	@Environmental
	private PageRenderSupport support;

	@Inject
	private SymbolSource symbolSource;

	@Inject
	private AssetSource assetSource;

	@Parameter
	private boolean disabled;

	@Parameter(name = "cacheKey", value = "prop:resources.completeId", defaultPrefix = "literal")
	private String cacheKeyBase;

	@Parameter(name = "cacheKeyExtra")
	private String cacheKeyExtra;

	@Parameter(name = "timeout", required = true, defaultPrefix = "literal")
	private String timeoutString;

	@Parameter(name = "lastUpdated")
	private Date lastUpdated;

	@SetupRender
	public void setupRender() {
		HttpServletRequest request = requestGlobals.getHTTPServletRequest();
		String mapKey = getMapKey( request );
		cache = mapFactory.get( mapKey );
	}

	@BeginRender
	public boolean beginRender( MarkupWriter writer ) {
		if ( !disabled ) {
			key = getCacheKey( cacheKeyBase, cacheKeyExtra );
			cv = cache.get( key );
			if ( cv != null && !cv.expired() ) {
				logger.debug( "BUFFER-HIT: " + key );
				return false;
			}

			logger.debug( "BUFFER-MISS: " + key );
			// TODO: LOCK TO RENDER ONCE ACROSS CLUSTER

			BufferPageRenderSupport bprs = new BufferPageRenderSupport( symbolSource, assetSource );
			environment.push( PageRenderSupport.class, bprs );
		}
		return true;
	}

	@AfterRender
	public void afterRender( MarkupWriter writer ) {
		if ( disabled ) {
			updateLastUpdated( System.currentTimeMillis() );
			return;
		}

		if ( cv != null ) {
			BufferValue bv = cv.getValue();
			bv.apply( writer, support, symbolSource, assetSource );
			updateLastUpdated( cv.getLastUpdated() );
		}
	}

	@BeforeRenderBody
	public void beforeRenderBody( MarkupWriter writer ) {
		if ( disabled ) {
			// do nothing extra
			return;
		}

		// create element to capture body
		writer.element( "div" );
	}

	@AfterRenderBody
	public void afterRenderBody( MarkupWriter writer ) {
		if ( disabled ) {
			// do nothing extra
			return;
		}

		// capture div
		Element element = writer.getElement();

		// close div
		writer.end();

		// capture div value
		String value = getValue( element );

		{
			// remove div
			Element parent = writer.getElement();
			parent.getChildren().remove( element );
		}

		{
			// capture body into cachevalue
			BufferPageRenderSupport bprs = (BufferPageRenderSupport) environment.peekRequired( PageRenderSupport.class );

			cv = new CacheValue();
			cv.setTimeoutSeconds( getTimeout() );
			cv.setValue( new BufferValue( value, bprs ) );
			cv.setLastUpdated( System.currentTimeMillis() );
			if ( cv.getTimeoutSeconds() > 0 ) {
				cache.put( key, cv );
			} else {
				cache.remove( key );
			}
		}
	}

	@AfterRender
	public void afterRender() {
		if ( !disabled ) {
			PageRenderSupport sp = environment.peek( PageRenderSupport.class );
			if ( sp instanceof BufferPageRenderSupport ) {
				environment.pop( PageRenderSupport.class );
			}
		}
	}

	public String getValue( Element element ) {
		CharArrayWriter cwriter = new CharArrayWriter();
		PrintWriter pwriter = new PrintWriter( cwriter );
		for ( Node child : element.getChildren() ) {
			child.toMarkup( pwriter );
		}
		pwriter.close();
		cwriter.close();
		return cwriter.toString();
	}

	// HELPERS

	public static String getMapKey( HttpServletRequest request ) {
		return request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
	}

	public static String getCacheKey( Object cacheKeyBase, Object cacheKeyExtra ) {
		if ( cacheKeyExtra != null ) {
			return cacheKeyBase + "_" + cacheKeyExtra;
		}
		return String.valueOf( cacheKeyBase );
	}

	public long getTimeout() {
		String timeout = timeoutString;
		long h = Long.parseLong( timeout.substring( 0, 2 ) );
		long m = Long.parseLong( timeout.substring( 3, 5 ) );
		long s = Long.parseLong( timeout.substring( 6, 8 ) );
		return s + m * 60 + h * 3600;
	}

	public void updateLastUpdated( long date ) {
		if ( resources.isBound( "lastUpdated" ) ) {
			lastUpdated = new Date( date );
		}
	}

	/*
	 * public static String clearCacheEntry( Object keybase, Object keyextra, HttpServletRequest request ) { PCached cache = null; String key = getCacheKey( keybase,
	 * keyextra, request, cache ); logger.debug( "BUFFER-CLEAR: " + key ); cache.delete( key ); return key; }
	 */
}
package ...;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.apache.tapestry.Asset;
import org.apache.tapestry.MarkupWriter;
import org.apache.tapestry.PageRenderSupport;
import org.apache.tapestry.ioc.services.SymbolSource;
import org.apache.tapestry.services.AssetSource;

public class BufferServices {

	public static class BufferValue extends BaseObject implements Serializable {

		private String body;
		private String script;
		private List<String> scripts;
		private List<String> styles;

		public BufferValue( String body, String script, List<String> scripts, List<String> styles ) {
			this.body = body;
			this.script = script;
			this.scripts = scripts;
			this.styles = styles;
		}

		public BufferValue( String body, BufferPageRenderSupport support ) {
			this( body, support.script.toString(), support.scripts, support.styles );
		}

		public String getBody() {
			return body;
		}

		public void setBody( String body ) {
			this.body = body;
		}

		public void apply( MarkupWriter writer, PageRenderSupport support, SymbolSource symbolSource, AssetSource assetSource ) {
			writer.writeRaw( getBody() );
			for ( String path : styles ) {
				String expanded = symbolSource.expandSymbols( path );
				Asset asset = assetSource.getAsset( null, expanded, null );
				support.addStylesheetLink( asset, null );
			}
			for ( String path : scripts ) {
				String expanded = symbolSource.expandSymbols( path );
				Asset asset = assetSource.getAsset( null, expanded, null );
				support.addScriptLink( asset );
			}
			support.addScript( script.replace( "%", "%%" ) );
		}

	}



	public static class CacheValue extends BaseObject implements Serializable {

		private long lastUpdated;
		private long timeout;
		private BufferValue value;

		public long getLastUpdated() {
			return lastUpdated;
		}

		public void setLastUpdated( long value ) {
			this.lastUpdated = value;
		}

		public long getTimeoutSeconds() {
			return timeout;
		}

		public void setTimeoutSeconds( long value ) {
			this.timeout = value;
		}

		public BufferValue getValue() {
			return value;
		}

		public void setValue( BufferValue value ) {
			this.value = value;
		}

		public boolean expired() {
			return lastUpdated + ( timeout * 1000 ) < System.currentTimeMillis();
		}

	}



	public static class BufferPageRenderSupport extends BaseObject implements PageRenderSupport {

		private SymbolSource symbolSource;
		private AssetSource assetSource;

		private StringBuilder script = new StringBuilder();
		private List<String> scripts = new ArrayList<String>();
		private List<String> styles = new ArrayList<String>();

		public BufferPageRenderSupport( SymbolSource symbolSource, AssetSource assetSource ) {
			this.symbolSource = symbolSource;
			this.assetSource = assetSource;
		}

		public void addClasspathScriptLink( String... classpaths ) {
			for ( String path : classpaths ) {
				String expanded = symbolSource.expandSymbols( path );
				Asset asset = assetSource.getAsset( null, expanded, null );
				addScriptLink( asset );
			}
		}

		public void addScript( String format, Object... arguments ) {
			script.append( String.format( format, arguments ) );
		}

		public void addScriptLink( Asset... scriptAssets ) {
			for ( Asset script : scriptAssets ) {
				String url = script.getResource().toString();
				if ( !scripts.contains( url ) ) {
					scripts.add( url );
				}
			}
		}

		public void addStylesheetLink( Asset stylesheet, String media ) {
			String url = stylesheet.getResource().toString();
			if ( !styles.contains( url ) ) {
				styles.add( url );
			}
		}

		public String allocateClientId( String id ) {
			Random random = new Random();
			return id + "_" + System.currentTimeMillis() + "_" + random.nextInt();
		}

	}

}

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to