You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2012/05/16 20:50:31 UTC
[2/44] git commit: Support caching of exists(), forLocale(),
and toURL() for Resources - Uses ReentrantReadWriteLock for high
throughput Fix incorrect version numbers for previous batch of changes
Support caching of exists(), forLocale(), and toURL() for Resources
- Uses ReentrantReadWriteLock for high throughput
Fix incorrect version numbers for previous batch of changes
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/6bd95286
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/6bd95286
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/6bd95286
Branch: refs/heads/master
Commit: 6bd9528652cd6359a0b4b915871204a604ce4399
Parents: a824b8d
Author: Howard M. Lewis Ship <hl...@gmail.com>
Authored: Tue May 15 10:56:52 2012 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Wed May 16 11:50:15 2012 -0700
----------------------------------------------------------------------
.../org/apache/tapestry5/ComponentResources.java | 6 +-
.../internal/services/ContextResource.java | 84 +++++++---
.../apache/tapestry5/internal/structure/Page.java | 6 +-
.../internal/structure/PageResetListener.java | 2 +-
.../tapestry5/runtime/PageLifecycleAdapter.java | 2 +-
.../runtime/PageLifecycleCallbackHub.java | 8 +-
.../tapestry5/runtime/PageLifecycleListener.java | 4 +-
.../java/org/apache/tapestry5/ioc/Resource.java | 14 +-
.../ioc/internal/util/AbstractResource.java | 131 ++++++++++++++-
.../ioc/internal/util/ClasspathResource.java | 42 ++++-
10 files changed, 245 insertions(+), 54 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java b/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
index 4e7bb8f..fcc1c7c 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/ComponentResources.java
@@ -173,7 +173,7 @@ public interface ComponentResources extends ComponentResourcesCommon
/**
* Adds a listener object that will be notified about page lifecycle events.
*
- * @deprecated In 5.3.3, use {@link #getPageLifecycleCallbackHub()} instead
+ * @deprecated In 5.3.4, use {@link #getPageLifecycleCallbackHub()} instead
*/
void addPageLifecycleListener(PageLifecycleListener listener);
@@ -181,7 +181,7 @@ public interface ComponentResources extends ComponentResourcesCommon
* Provides access to an object that can be used to register callbacks for page lifecycle events.
*
* @return the hub
- * @since 5.3.3
+ * @since 5.3.4
*/
PageLifecycleCallbackHub getPageLifecycleCallbackHub();
@@ -189,7 +189,7 @@ public interface ComponentResources extends ComponentResourcesCommon
* Removes a previously added listener.
*
* @since 5.2.0
- * @deprecated in 5.3.3, not necessary with {@link PageLifecycleCallbackHub#addPageLoadedCallback(Runnable)}.
+ * @deprecated in 5.3.4, not necessary with {@link PageLifecycleCallbackHub#addPageLoadedCallback(Runnable)}.
*/
void removePageLifecycleListener(PageLifecycleListener listener);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
index bb0e4e9..7d2db59 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ContextResource.java
@@ -14,14 +14,14 @@
package org.apache.tapestry5.internal.services;
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.internal.util.AbstractResource;
import org.apache.tapestry5.services.Context;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
/**
* A resource stored with in the web application context.
*/
@@ -31,10 +31,16 @@ public class ContextResource extends AbstractResource
private final Context context;
+ // Guarded by lock
+ private URL url;
+
+ // Guarded by lock
+ private boolean urlResolved;
+
public ContextResource(Context context, String path)
{
super(path);
-
+
assert context != null;
this.context = context;
@@ -54,33 +60,69 @@ public class ContextResource extends AbstractResource
public URL toURL()
{
- // This is so easy to screw up; ClassLoader.getResource() doesn't want a leading slash,
- // and HttpServletContext.getResource() does. This is what I mean when I say that
- // a framework is an accumulation of the combined experience of many users and developers.
+ try
+ {
+ lock.readLock().lock();
- String contextPath = "/" + getPath();
+ if (!urlResolved)
+ {
+ resolveURL();
+ }
- // Always prefer the actual file to the URL. This is critical for templates to
- // reload inside Tomcat.
+ return url;
- File file = context.getRealFile(contextPath);
+ } finally
+ {
+ lock.readLock().unlock();
+ }
+ }
- if (file != null && file.exists())
+ private void resolveURL()
+ {
+ try
{
- try
+ upgradeReadLockToWriteLock();
+
+ // Race condition on the write lock:
+ if (urlResolved)
{
- return file.toURL();
+ return;
}
- catch (MalformedURLException ex)
+
+ // This is so easy to screw up; ClassLoader.getResource() doesn't want a leading slash,
+ // and HttpServletContext.getResource() does. This is what I mean when I say that
+ // a framework is an accumulation of the combined experience of many users and developers.
+
+ String contextPath = "/" + getPath();
+
+ // Always prefer the actual file to the URL. This is critical for templates to
+ // reload inside Tomcat.
+
+ File file = context.getRealFile(contextPath);
+
+ if (file != null && file.exists())
{
- throw new RuntimeException(ex);
+ try
+ {
+ url = file.toURL();
+ urlResolved = true;
+ return;
+ } catch (MalformedURLException ex)
+ {
+ throw new RuntimeException(ex);
+ }
}
- }
- // But, when packaged inside a WAR or JAR, the File will not be available, so use whatever
- // URL we get ... but reloading won't work.
+ // But, when packaged inside a WAR or JAR, the File will not be available, so use whatever
+ // URL we get ... but reloading won't work.
- return context.getResource(contextPath);
+ url = context.getResource(contextPath);
+ urlResolved = true;
+
+ } finally
+ {
+ downgradeWriteLockToReadLock();
+ }
}
@Override
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
index ae66822..845f0a4 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/Page.java
@@ -143,7 +143,7 @@ public interface Page extends PageLifecycleCallbackHub
/**
* Adds a listener that is notified of large scale page events.
*
- * @deprecated in 5.3.3; use {@link #addPageLoadedCallback(Runnable)}, {@link #addPageAttachedCallback(Runnable)}, or
+ * @deprecated in 5.3.4; use {@link #addPageLoadedCallback(Runnable)}, {@link #addPageAttachedCallback(Runnable)}, or
* {@link #addPageDetachedCallback(Runnable)} instead
*/
void addLifecycleListener(PageLifecycleListener listener);
@@ -152,7 +152,7 @@ public interface Page extends PageLifecycleCallbackHub
* Removes a listener that was previously added.
*
* @since 5.2.0
- * @deprecated in 5.3.3, due to introduction of {@link #addPageLoadedCallback(Runnable)}
+ * @deprecated in 5.3.4, due to introduction of {@link #addPageLoadedCallback(Runnable)}
*/
void removeLifecycleListener(PageLifecycleListener listener);
@@ -213,7 +213,7 @@ public interface Page extends PageLifecycleCallbackHub
* @param listener
* will receive notifications when the page is accessed from a different page
* @since 5.2.0
- * @deprecated in 5.3.3,
+ * @deprecated in 5.3.4,
*/
void addResetListener(PageResetListener listener);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
index ad95d29..055c907 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/structure/PageResetListener.java
@@ -21,7 +21,7 @@ import org.apache.tapestry5.annotations.PageReset;
*
* @since 5.2.0
* @see PageReset
- * @deprecated in 5.3.3
+ * @deprecated in 5.3.4
* @see org.apache.tapestry5.runtime.PageLifecycleCallbackHub#addResetCallback(Runnable)
*/
public interface PageResetListener
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
index e7f45ff..cb5b891 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleAdapter.java
@@ -17,7 +17,7 @@ package org.apache.tapestry5.runtime;
/**
* Empty implementation of the {@link PageLifecycleListener} interface.
*
- * @deprecated in 5.3.3, as {@link PageLifecycleListener} has been deprecated
+ * @deprecated in 5.3.4, as {@link PageLifecycleListener} has been deprecated
*/
public class PageLifecycleAdapter implements PageLifecycleListener
{
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
index 518925e..8d10098 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleCallbackHub.java
@@ -18,7 +18,7 @@ package org.apache.tapestry5.runtime;
* Defines a way for different aspects of a page to add callbacks for important lifecycle events.
*
* @see org.apache.tapestry5.ComponentResources#getPageLifecycleCallbackHub()
- * @since 5.3.3
+ * @since 5.3.4
*/
public interface PageLifecycleCallbackHub
{
@@ -28,7 +28,7 @@ public interface PageLifecycleCallbackHub
*
* @param callback
* invoked once, when page is first loaded
- * @since 5.3.3
+ * @since 5.3.4
*/
void addPageLoadedCallback(Runnable callback);
@@ -36,7 +36,7 @@ public interface PageLifecycleCallbackHub
* Adds a callback for when the page is attached to the request.
*
* @param callback
- * @since 5.3.3
+ * @since 5.3.4
*/
void addPageAttachedCallback(Runnable callback);
@@ -44,7 +44,7 @@ public interface PageLifecycleCallbackHub
* Adds a callback for when the page is detached from the request.
*
* @param callback
- * @since 5.3.3
+ * @since 5.3.4
*/
void addPageDetachedCallback(Runnable callback);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
index 5e80cec..b1d5405 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/runtime/PageLifecycleListener.java
@@ -19,7 +19,7 @@ package org.apache.tapestry5.runtime;
* {@linkplain org.apache.tapestry5.plastic.PlasticClass#introduceInterface(Class)} introduced}, the component will
* automatically register itself as a listener with the page.
*
- * @deprecated in 5.3.3, replaced with {@link PageLifecycleCallbackHub}
+ * @deprecated in 5.3.4, replaced with {@link PageLifecycleCallbackHub}
*/
public interface PageLifecycleListener
{
@@ -27,7 +27,7 @@ public interface PageLifecycleListener
* Invoked when the page finishes loading. This occurs once all components are loaded and all parameters have been
* set.
*
- * @deprecated in 5.3.3, use {@link org.apache.tapestry5.ComponentResources#addPageLoadedCallback(Runnable)} instead
+ * @deprecated in 5.3.4, use {@link org.apache.tapestry5.ComponentResources#addPageLoadedCallback(Runnable)} instead
*/
void containingPageDidLoad();
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java
index aa431f5..7a73fd7 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/Resource.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008 The Apache Software Foundation
+// Copyright 2006, 2008, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -30,7 +30,8 @@ public interface Resource
{
/**
- * Returns true if the resource exists; if a stream to the content of the file may be openned.
+ * Returns true if the resource exists; if a stream to the content of the file may be opened. A resource exists
+ * if {@link #toURL()} returns a non-null value. Starting in release 5.3.4, the result of this is cached.
*
* @return true if the resource exists, false if it does not
*/
@@ -44,12 +45,14 @@ public interface Resource
InputStream openStream() throws IOException;
/**
- * Returns the URL for the resource, or null if it does not exist.
+ * Returns the URL for the resource, or null if it does not exist. This value is lazily computed; starting in 5.3.4, subclasses may cache
+ * the result.
*/
URL toURL();
/**
- * Returns a localized version of the resource. May return null if no such resource exists.
+ * Returns a localized version of the resource. May return null if no such resource exists. Starting in release
+ * 5.3.4, the result of this method is cached internally.
*/
Resource forLocale(Locale locale);
@@ -63,7 +66,8 @@ public interface Resource
* Returns a new Resource with the extension changed (or, if the resource does not have an extension, the extension
* is added). The new Resource may not exist (that is, {@link #toURL()} may return null.
*
- * @param extension to apply to the resource, such as "html" or "properties"
+ * @param extension
+ * to apply to the resource, such as "html" or "properties"
* @return the new resource
*/
Resource withExtension(String extension);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
index d376514..f873eec 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/AbstractResource.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008, 2010, 2011 The Apache Software Foundation
+// Copyright 2006, 2008, 2010, 2011, 2012 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,14 +14,15 @@
package org.apache.tapestry5.ioc.internal.util;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.util.LocalizedNameGenerator;
+
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Locale;
-
-import org.apache.tapestry5.ioc.Resource;
-import org.apache.tapestry5.ioc.util.LocalizedNameGenerator;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Abstract implementation of {@link Resource}. Subclasses must implement the abstract methods {@link Resource#toURL()}
@@ -29,8 +30,35 @@ import org.apache.tapestry5.ioc.util.LocalizedNameGenerator;
*/
public abstract class AbstractResource implements Resource
{
+ private class Localization
+ {
+ final Locale locale;
+
+ final Resource resource;
+
+ final Localization next;
+
+ private Localization(Locale locale, Resource resource, Localization next)
+ {
+ this.locale = locale;
+ this.resource = resource;
+ this.next = next;
+ }
+ }
+
private final String path;
+ /**
+ * A lock used to when lazily computing other values.
+ */
+ protected final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+ // Guarded by Lock
+ private boolean exists, existsComputed;
+
+ // Guarded by lock
+ private Localization firstLocalization;
+
protected AbstractResource(String path)
{
assert path != null;
@@ -99,6 +127,69 @@ public abstract class AbstractResource implements Resource
public final Resource forLocale(Locale locale)
{
+ try
+ {
+ lock.readLock().lock();
+
+ for (Localization l = firstLocalization; l != null; l = l.next)
+ {
+ if (l.locale.equals(locale))
+ {
+ return l.resource;
+ }
+ }
+
+ return populateLocalizationCache(locale);
+ } finally
+ {
+ lock.readLock().unlock();
+ }
+ }
+
+ private Resource populateLocalizationCache(Locale locale)
+ {
+ try
+ {
+ upgradeReadLockToWriteLock();
+
+ // Race condition: another thread may have beaten us to it:
+
+ for (Localization l = firstLocalization; l != null; l = l.next)
+ {
+ if (l.locale.equals(locale))
+ {
+ return l.resource;
+ }
+ }
+
+ Resource result = findLocalizedResource(locale);
+
+ firstLocalization = new Localization(locale, result, firstLocalization);
+
+ return result;
+
+ } finally
+ {
+ downgradeWriteLockToReadLock();
+ }
+ }
+
+ protected final void upgradeReadLockToWriteLock()
+ {
+ lock.readLock().unlock();
+ // This is that instant where another thread may grab the write lock. Very rare, but possible.
+ lock.writeLock().lock();
+ }
+
+ protected final void downgradeWriteLockToReadLock()
+ {
+
+ lock.readLock().lock();
+ lock.writeLock().unlock();
+ }
+
+ private Resource findLocalizedResource(Locale locale)
+ {
for (String path : new LocalizedNameGenerator(this.path, locale))
{
Resource potential = createResource(path);
@@ -138,7 +229,37 @@ public abstract class AbstractResource implements Resource
*/
public boolean exists()
{
- return toURL() != null;
+ try
+ {
+ lock.readLock().lock();
+
+ if (!existsComputed)
+ {
+ computeExists();
+ }
+
+ return exists;
+ } finally
+ {
+ lock.readLock().unlock();
+ }
+ }
+
+ private void computeExists()
+ {
+ try
+ {
+ upgradeReadLockToWriteLock();
+
+ if (!existsComputed)
+ {
+ exists = toURL() != null;
+ existsComputed = true;
+ }
+ } finally
+ {
+ downgradeWriteLockToReadLock();
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6bd95286/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
index 85eb8cb..a6d21ae 100644
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
+++ b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/ClasspathResource.java
@@ -14,10 +14,10 @@
package org.apache.tapestry5.ioc.internal.util;
-import java.net.URL;
-
import org.apache.tapestry5.ioc.Resource;
+import java.net.URL;
+
/**
* Implementation of {@link Resource} for files on the classpath (as defined by a {@link ClassLoader}).
*/
@@ -25,10 +25,10 @@ public final class ClasspathResource extends AbstractResource
{
private final ClassLoader classLoader;
- // Guarded by this
+ // Guarded by lock
private URL url;
- // Guarded by this
+ // Guarded by lock
private boolean urlResolved;
public ClasspathResource(String path)
@@ -50,15 +50,39 @@ public final class ClasspathResource extends AbstractResource
return new ClasspathResource(classLoader, path);
}
- public synchronized URL toURL()
+ public URL toURL()
{
- if (!urlResolved)
+ try
+ {
+ lock.readLock().lock();
+
+ if (!urlResolved)
+ {
+ resolveURL();
+ }
+
+ return url;
+ } finally
{
- url = classLoader.getResource(getPath());
- urlResolved = true;
+ lock.readLock().unlock();
}
+ }
- return url;
+ private void resolveURL()
+ {
+ try
+ {
+ upgradeReadLockToWriteLock();
+
+ if (!urlResolved)
+ {
+ url = classLoader.getResource(getPath());
+ urlResolved = true;
+ }
+ } finally
+ {
+ downgradeWriteLockToReadLock();
+ }
}
@Override