net.java.dev.weblets.packaged.WebletResourceloadingUtils Maven / Gradle / Ivy
package net.java.dev.weblets.packaged;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import net.java.dev.weblets.WebletConfig;
import net.java.dev.weblets.WebletRequest;
import net.java.dev.weblets.WebletResponse;
import net.java.dev.weblets.util.CopyStrategy;
import net.java.dev.weblets.util.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* helper class to be shared by various weblet loaders
*
* @author Werner Punz
*/
public class WebletResourceloadingUtils {
static WebletResourceloadingUtils instance = new WebletResourceloadingUtils();
private static final long MILLIS_PER_YEAR = 1000l * 60l * 60l * 24l * 365l;
static final int CACHED_URLS = 3000;
static final String CACHE_KEY = "WEBLET_CACHE";
public URL getResourceUrl(WebletRequest request, String resourcePath) {
Map urlCache = getResourceURLCache(request);
URL url = null;
url = (URL) urlCache.get(resourcePath);
if(url != null)
return url;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
url = loader.getResource(resourcePath);
if (url == null) {
loader = getClass().getClassLoader();
url = loader.getResource(resourcePath);
}
urlCache.put(resourcePath, url);
return url;
}
/**
* fetches the resource url from a given resource path (uncached for reporting only) TODO add a better cache (LRU if possible)
*
* @param resourcePath
* @return
*/
public URL getResourceUrl(String resourcePath) {
URL url = null;
if (url != null)
return url;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
url = loader.getResource(resourcePath);
if (url == null) {
loader = getClass().getClassLoader();
url = loader.getResource(resourcePath);
}
return url;
}
/* entry cache per session */
private Map getResourceURLCache(WebletRequest request) {
HttpSession session = ((HttpServletRequest) request.getExternalRequest()).getSession();
// session
Map cache = (Map) session.getAttribute(CACHE_KEY);
if (cache == null) {
cache = Collections.synchronizedMap(new HashMap(CACHED_URLS));
session.setAttribute(CACHE_KEY, cache);
}
if (cache.size() >= CACHED_URLS) {
cache.clear();
}
return cache;
};
public static WebletResourceloadingUtils getInstance() {
return instance;
}
/**
* loads a resource from a given url
*
* @param config
* the current weblet config
* @param request
* the current weblet request
* @param response
* the current weblet response
* @param url
* the current url
* @param copyProvider
* the processing filter chain for the weblet serving
*/
public void loadFromUrl(WebletConfig config, WebletRequest request, WebletResponse response, URL url, CopyStrategy copyProvider) throws IOException {
if (url != null) {
URLConnection conn = url.openConnection();
long lastmodified = conn.getLastModified();
//length not really working because the content length can be different
//due to the filter post processing we have to cache it in ram first
//or use the latest trunks weblets caching before serving
//which should go hand in hand with gzipping upfron
//determineContentLength(response, url);
loadResourceFromStream(config, request, response, copyProvider, conn.getInputStream(), lastmodified);
} else {
response.setStatus(WebletResponse.SC_NOT_FOUND);
}
}
private void determineContentLength(WebletResponse response, URL url) {
try {
File f = new File(url.toURI());
response.setContentLength((long) f.length());
} catch (URISyntaxException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
/**
* sets initial response params upon the version state
*
* @param config
* the weblet config
* @param response
* the weblet response
* @param lastmodified
* local resource lastmodified
*/
private void prepareVersionedResponse(WebletConfig config, WebletResponse response, long lastmodified, long timeout) {
String webletVersion = config.getWebletVersion();
if (!isVersionedWeblet(webletVersion)) {
response.setLastModified(lastmodified);
/*
* set the expires and content version in the head
*/
response.setContentVersion((webletVersion == null) ? "" : webletVersion, getPast());
} else {
// we lock out resource loading once versioned, we do not run
// into the chain
// just in case the expires is ignored
// this enforces the loading from cache on some browsers
// even if refresh is pressed, this is by
// definition the wanted behavior if versioning is on!
// some browsers like firefox despite
// having a future number pass a local date on refresh maybe we
// lock this out as well
response.setLastModified(lastmodified);
// this should prevent requests entirely!
response.setContentVersion(webletVersion, timeout);
}
}
private long getTimeout(WebletConfig config) {
String cacheControlTimeout = config.getInitParameter("cachecontrol-timeout");
long timeout = WebletResourceloadingUtils.getNever();
if (!StringUtils.isBlank(cacheControlTimeout)) {
try {
timeout = Long.parseLong(cacheControlTimeout);
} catch (RuntimeException ex) {
Log log = LogFactory.getLog(this.getClass());
log.error("Weblets: Cache control is set but to an invalid value setting now never instead");
}
}
return timeout;
}
/**
* fixes internal time values browsers deliver time values on seconds internally it is calculated in miliseconds
*
* @param browserTimeValue
* the browser time value
* @return returns a fixed second time value for the input
*/
private long fixTimeValue(long browserTimeValue) {
// some browsers only work on seconds (Mozilla) so we go down to one
// second for a shared
// common response time
// we cannot tamper the cache state here, because
// otherwise firefox will fail with an emptied page resource cache
// (shift f5)
if (browserTimeValue > 1000)
browserTimeValue = browserTimeValue - browserTimeValue % 1000;
return browserTimeValue;
}
/**
* loads a given resource from an input stream it uses internal timestamps for resource handling and resource serving this works on most browser but safari
* seems to ignore the timestamps and always sends a modifiedSince for resources for 1.1.1970
*
* @param config
* the weblets config for this resource loading request
* @param request
* the weblets request
* @param response
* the weblets response
* @param copyProvider
* a given processing copy provider
* @param in
* the input stream for the processing
* @param resourceLastmodified
* the lastmodified for the given input stream
* @throws IOException
* in case of an internal processing error
*/
public void loadResourceFromStream(WebletConfig config, WebletRequest request, WebletResponse response, CopyStrategy copyProvider, InputStream in,
long resourceLastmodified) throws IOException {
if (in != null) {
// mime-type
String webletVersion = config.getWebletVersion();
boolean load = false;
if (isVersionedWeblet(webletVersion)) {
//we check if the resource last modified + timeout < currentTime, thats the only condition we cannot
//Serve a resource served, we have a strong
long requestCacheState = request.getIfModifiedSince();
// the browser sends the utc timestamp
requestCacheState = fixTimeValue(requestCacheState);
long currentTime = System.currentTimeMillis();
// utc time mapping
long currentUTCTime = currentTime - TimeZone.getDefault().getOffset(currentTime);
currentUTCTime = fixTimeValue(currentUTCTime);
//a request cache state of -1 means no resurce loaded so we have to load under any condition
load = requestCacheState == -1 || (requestCacheState + getTimeout(config)) < currentUTCTime;
} else {
long requestCacheState = request.getIfModifiedSince();
resourceLastmodified = fixTimeValue(resourceLastmodified);
long utcResourceModifiedState = (resourceLastmodified - TimeZone.getDefault().getOffset(resourceLastmodified));
load = requestCacheState < utcResourceModifiedState;
}
//long requestCacheState = request.getIfModifiedSince();
// the browser sends the utc timestamp
//requestCacheState = fixTimeValue(requestCacheState);
//long resourceModifiedState = resourceLastmodified;
//resourceModifiedState = fixTimeValue(resourceModifiedState);
//boolean load = false;
//long currentTime = System.currentTimeMillis();
// utc time mapping
//long currentUTCTime = currentTime - TimeZone.getDefault().getOffset(currentTime);
//currentUTCTime = fixTimeValue(currentUTCTime);
//long utcResourceModifiedState = (resourceModifiedState - TimeZone.getDefault().getOffset(resourceModifiedState)); // + getTimeout(config);
//utcResourceModifiedState = fixTimeValue(utcResourceModifiedState);
//load = (requestCacheState < utcResourceModifiedState)
/*-1 or smaller value on reload pressed*/
//|| (requestCacheState + getTimeout(config)) < currentUTCTime;
/* cache control timeout reached we reload no matter what! */
if (load) {
prepareVersionedResponse(config, response, resourceLastmodified, System.currentTimeMillis() + getTimeout(config));
//response.setContentType(finalMimetype);
loadResourceFromStream(config, request, response, copyProvider, in);
// response.setStatus(200);
} else {
/* we have to set the timestamps as well here */
prepareVersionedResponse(config, response, resourceLastmodified, request.getIfModifiedSince()
+ getTimeout(config));
//response.setContentType(null); // Bogus "text/html" overriding
response.setStatus(WebletResponse.SC_NOT_MODIFIED);
}
} else {
response.setStatus(WebletResponse.SC_NOT_FOUND);
}
}
/**
* loads the resource from a given input stream note, this api is under construction we have caching not enabled yet
*
* @param config
* the weblet config to load the resource
* @param request
* the weblet request
* @param response
* the weblet response
* @param copyProvider
* the processing copy provider
* @param in
* the resource serving input stream
* @throws IOException
*/
public void loadResourceFromStream(WebletConfig config, WebletRequest request, WebletResponse response, CopyStrategy copyProvider, InputStream in)
throws IOException {
OutputStream out = response.getOutputStream();
String finalMimetype = config.getMimeType(request.getPathInfo());
copyProvider.copy(request.getWebletName(), finalMimetype, in, out);
}
/* unified version checker for weblet versions maybe in existence */
public static boolean isVersionedWeblet(String webletVersion) {
return webletVersion != null && !webletVersion.trim().equals("") && !webletVersion.endsWith("-SNAPSHOT");
}
/* defined never value used system internally */
public static long getNever() {
long now = System.currentTimeMillis();
return now + MILLIS_PER_YEAR;
}
public static long getPast() {
long now = System.currentTimeMillis();
return now - MILLIS_PER_YEAR;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy