All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.got5.tapestry5.jquery.services.js.JSLocatorImpl Maven / Gradle / Ivy

The newest version!
package org.got5.tapestry5.jquery.services.js;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Session;
import org.slf4j.Logger;

/*
 * Simple cache. Should be OK for single webservers
 * with high script reuse
 * 
 * TODO
 * create 3 maps
 * newScripts
 * reusedScripts
 * removedScripts
 * 
 * This will allow better management and remove the race condition
 * 
 * Would it be better to store this in the Session?
 */
public class JSLocatorImpl implements JSLocator {
	private final Map scripts = new HashMap(1000);
	private final Logger logger;
	private final Request request;
	
	public JSLocatorImpl(Logger logger, Request request) {
		this.logger = logger;
		this.request = request;
		Session session = request.getSession(true);
	}
	
	public String store(final String script) {
		String key = createKey(script);
		Content content = scripts.get(key);
		if ( content != null ) {
			content.reuse();
			return keyToUrl(key);
		}
		scripts.put(key, new Content(script));
		// only flush if we are making the cache too big
		if ( scripts.size() > 900 ) {
			flush();
		}
		return keyToUrl(key);
	}

	public String find(String path) {
		return scripts.get(path).getScript();
	}
	
	private synchronized void flush() {
		long time = new Date().getTime() - 1000*60*5; //5 minutes
		List remove = new ArrayList(scripts.size());
		for ( Entry entry : scripts.entrySet()) {
			if ( entry.getValue().isExpired(time) ) {
				remove.add(entry.getKey());			
			}		
		}
		logger.info("size {} remove {}",scripts.size(),remove.size());
		for ( String key : remove ) {
			Content content = scripts.get(key);
			// keep them if reused. There is is still a race here but it's a short one
			// could put them in a removed map so they hang around a bit longer
			// then just clear that map
			if ( content.isExpired(time)) {
				scripts.remove(key);
			}
		}
	}
	
	private String createKey(String script) {
		return new String(Hex.encodeHex(DigestUtils.sha(script)));
	}
	
	public String keyToUrl(String key) {		
		return String.format("%s/js/%s",request.getContextPath(), key);
	}
	
	class Content {
		private long date;
		private final String script;
		private boolean reused = false;
		
		Content(String script) {
			this.script = script;
			date = new Date().getTime();
		}
		// hang on to static ones
		boolean isExpired(long time) {
			if ( reused && scripts.size() < 990) {
				return false;
			}
			return date < time;
		}
		
		String getScript() {
			return script;
		}
		
		void reuse() {
			date = new Date().getTime();
			reused = true;
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy