Index: plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/SxResourceSource.java
===================================================================
--- plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/SxResourceSource.java (revision 25712)
+++ plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/SxResourceSource.java (working copy)
@@ -31,7 +31,7 @@
* @version $Id$
* @since 1.7M2
*/
-public class SxResourceSource implements SxSource
+public class SxResourceSource extends AbstractSxSource
{
/**
* The full path of the resource to use as extension. Exemple: path/to/hello.js
Index: plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/SxDocumentSource.java
===================================================================
--- plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/SxDocumentSource.java (revision 25712)
+++ plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/SxDocumentSource.java (working copy)
@@ -27,6 +27,7 @@
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;
+import org.xwiki.bridge.DocumentName;
/**
* Wiki Document source for Skin Extensions. This is the standard source for Skin Extensions, using an XWiki object of
@@ -35,7 +36,7 @@
* @version $Id$
* @since 1.7M2
*/
-public class SxDocumentSource implements SxSource
+public class SxDocumentSource extends AbstractSxSource
{
/** The name of the property in the script extension object which contains the script content. */
private static final String CONTENT_PROPERTY_NAME = "code";
@@ -142,4 +143,14 @@
return this.document.getDate().getTime();
}
+ /**
+ * {@inheritDoc}
+ *
+ * @see SxSource#getDocumentName()
+ */
+ public DocumentName getDocumentName()
+ {
+ return this.document.getDocumentName();
+ }
+
}
Index: plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/SxSource.java
===================================================================
--- plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/SxSource.java (revision 25712)
+++ plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/SxSource.java (working copy)
@@ -20,6 +20,10 @@
*/
package com.xpn.xwiki.web.sx;
+import java.util.Date;
+
+import org.xwiki.bridge.DocumentName;
+
/**
* Skin extension source. Can be a document, a resource file, or anything else.
*
@@ -35,25 +39,46 @@
{
/**
* Cache for a long time.
- * @see AbstractSxAction#LONG_CACHE_DURATION
+ * @see AbstractSxSource#LONG_CACHE_DURATION
*/
- LONG,
+ LONG(30 * 24 * 3600 * 1000L),
/**
* Cache for a short time.
- * @see AbstractSxAction#SHORT_CACHE_DURATION
+ * @see AbstractSxSource#SHORT_CACHE_DURATION
*/
- SHORT,
+ SHORT(1 * 24 * 3600 * 1000L),
/**
* Cache for the proxy/browser's default time, It will be held in the
- * server cache an amount of time specified in AbstractSxAction.
- * @see AbstractSxAction.DEFAULT_CACHE_DURATION
+ * server cache an amount of time specified in AbstractSxSource.
+ * @see AbstractSxSource.DEFAULT_CACHE_DURATION
*/
- DEFAULT,
+ DEFAULT(1 * 24 * 3600 * 1000L),
/** Do not cache at all in server cache or in proxy/browser. */
- FORBID
+ FORBID(0x0000000000000000L);
+
+ /** How many milliseconds this cache should last for. */
+ private final long cacheDuration;
+
+ /**
+ * Private Constructor.
+ *
+ * @param duration How many milliseconds this cache should last for.
+ */
+ private CachePolicy(long duration)
+ {
+ cacheDuration = duration;
+ }
+
+ /**
+ * @return The time when caches with this policy should expire if they start now
+ */
+ public long getExpirationTime()
+ {
+ return new Date().getTime() + cacheDuration;
+ }
}
/**
@@ -71,4 +96,13 @@
*/
CachePolicy getCachePolicy();
+ /**
+ * @return The DocumentName of the document from which this script was taken or null of not applicable.
+ */
+ DocumentName getDocumentName();
+
+ /**
+ * @return The time when caches of this source should expire
+ */
+ long getExpirationTime();
}
Index: plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/AbstractSxAction.java
===================================================================
--- plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/AbstractSxAction.java (revision 25712)
+++ plugins/skinx/src/main/java/com/xpn/xwiki/web/sx/AbstractSxAction.java (working copy)
@@ -21,7 +21,6 @@
package com.xpn.xwiki.web.sx;
import java.io.IOException;
-import java.util.Date;
import javax.servlet.http.HttpServletResponse;
@@ -32,7 +31,6 @@
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.web.XWikiAction;
-import com.xpn.xwiki.web.XWikiRequest;
import com.xpn.xwiki.web.XWikiResponse;
import com.xpn.xwiki.web.sx.SxSource.CachePolicy;
@@ -45,12 +43,6 @@
*/
public abstract class AbstractSxAction extends XWikiAction
{
- /** How many milliseconds a file should be cached for if it sets CachePolicy to LONG, hardcoded to 30 days. */
- private static final long LONG_CACHE_DURATION = 30 * 24 * 3600 * 1000L;
-
- /** How many milliseconds a file should be cached for if it sets CachePolicy to SHORT, hardcoded to 1 day. */
- private static final long SHORT_CACHE_DURATION = 1 * 24 * 3600 * 1000L;
-
/** What http header parameter is used to specify when a file was last modified. */
private static final String LAST_MODIFIED_HEADER = "Last-Modified";
@@ -69,6 +61,9 @@
/** If the user specifies this url parameter equals false, we will send uncompressed script content. */
private static final String COMPRESS_SCRIPT_REQUEST_PARAMETER = "minify";
+ /** Static instance of SxCache, lazy initialized because we need the context to get the desired capacity. */
+ private static SxCache sxCache;
+
/** @return the logging object of the concrete subclass. */
protected abstract Log getLog();
@@ -84,7 +79,6 @@
public void renderExtension(SxSource sxSource, Extension sxType, XWikiContext context)
throws XWikiException
{
- XWikiRequest request = context.getRequest();
XWikiResponse response = context.getResponse();
String extensionContent = sxSource.getContent();
@@ -99,20 +93,14 @@
if (cachePolicy != CachePolicy.FORBID) {
response.setHeader(CACHE_CONTROL_HEADER, "public");
- }
- if (cachePolicy == CachePolicy.LONG) {
- // Cache for one month (30 days)
- response.setDateHeader(CACHE_EXPIRES_HEADER, (new Date()).getTime() + LONG_CACHE_DURATION);
- } else if (cachePolicy == CachePolicy.SHORT) {
- // Cache for one day
- response.setDateHeader(CACHE_EXPIRES_HEADER, (new Date()).getTime() + SHORT_CACHE_DURATION);
- } else if (cachePolicy == CachePolicy.FORBID) {
+ if (cachePolicy != CachePolicy.DEFAULT) {
+ response.setHeader(CACHE_EXPIRES_HEADER, (Long.toString(sxSource.getExpirationTime())));
+ }
+ } else {
response.setHeader(CACHE_CONTROL_HEADER, "no-cache, no-store, must-revalidate");
}
- if (BooleanUtils.toBoolean(StringUtils.defaultIfEmpty(
- request.get(COMPRESS_SCRIPT_REQUEST_PARAMETER), "true")))
- {
+ if (!(sxSource instanceof SxCacheableSource) && skipCompressionRequested(context)) {
extensionContent = sxType.getCompressor().compress(extensionContent);
}
@@ -133,16 +121,31 @@
@Override
public String render(XWikiContext context) throws XWikiException
{
- SxSource sxSource;
+ synchronized (this) {
+ if (sxCache == null) {
+ sxCache = SxCache.newInstance(context);
+ }
+ }
+ SxSource sxSource = sxCache.get(context.getURL().toString());
- if (context.getRequest().getParameter(JAR_RESOURCE_REQUEST_PARAMETER) != null) {
- sxSource = new SxResourceSource(context.getRequest().getParameter(JAR_RESOURCE_REQUEST_PARAMETER));
- } else {
- if (context.getDoc().isNew()) {
- context.getResponse().setStatus(HttpServletResponse.SC_NOT_FOUND);
- return "docdoesnotexist";
+ boolean userWantsUncompressed = skipCompressionRequested(context);
+ if (sxSource == null || userWantsUncompressed) {
+ if (context.getRequest().getParameter(JAR_RESOURCE_REQUEST_PARAMETER) != null) {
+ sxSource = new SxResourceSource(context.getRequest().getParameter(JAR_RESOURCE_REQUEST_PARAMETER));
+ } else {
+ if (context.getDoc().isNew()) {
+ context.getResponse().setStatus(HttpServletResponse.SC_NOT_FOUND);
+ return "docdoesnotexist";
+ }
+ sxSource = new SxDocumentSource(context, getExtensionType());
}
- sxSource = new SxDocumentSource(context, getExtensionType());
+ // There is no point in caching an uncompressed script since the cache is always compressed
+ // so if the user did not want an uncompressed script, we assume the cache needed updating.
+ if (!userWantsUncompressed) {
+ SxCacheableSource cacheSx = new DefaultSxCacheableSource(sxSource, getExtensionType());
+ sxCache.put(context.getURL().toString(), cacheSx);
+ sxSource = cacheSx;
+ }
}
try {
@@ -161,4 +164,22 @@
*/
public abstract Extension getExtensionType();
+ /**
+ * Get Whether or not the user has requested an uncompressed version of the script.
+ *
+ * @param context The XWikiContext
+ * @return does the user want an uncompressed (origional format) script?
+ */
+ private boolean skipCompressionRequested(XWikiContext context)
+ {
+ if (!BooleanUtils.toBoolean(
+ StringUtils.defaultIfEmpty(context.getRequest().get(COMPRESS_SCRIPT_REQUEST_PARAMETER), "true")))
+ {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("The user has requested an uncompressed version of a script");
+ }
+ return true;
+ }
+ return false;
+ }
}
Index: plugins/skinx/pom.xml
===================================================================
--- plugins/skinx/pom.xml (revision 25712)
+++ plugins/skinx/pom.xml (working copy)
@@ -40,7 +40,7 @@
com.xpn.xwiki.platform
xwiki-core
- ${platform.core.version}
+ 2.2-SNAPSHOT
provided
Index: tools/xwiki-configuration-resources/src/main/resources/xwiki.cfg.vm
===================================================================
--- tools/xwiki-configuration-resources/src/main/resources/xwiki.cfg.vm (revision 25712)
+++ tools/xwiki-configuration-resources/src/main/resources/xwiki.cfg.vm (working copy)
@@ -556,6 +556,14 @@
#-# Default: 0
# xwiki.plugin.activitystream.daystokeepevents=0
+#-# Skin Extension plugin.
+#-# The Skinx plugin provides a way to get scripts such as CSS or Javascript to the browser from objects attached to
+#-# or from resources within JAR files. The Skinx plugin impliments a cache which stores the scripts in minified form.
+#-#
+#-# The capacity (number of scripts which may be held) in the Skinx cache (Default is 50).
+# xwiki.plugin.skinx.cache.capacity=50
+
+
#---------------------------------------
# Misc
#