Hi,

Am 07.05.2014 22:44, schrieb Thiago H de Paula Figueiredo:
On Wed, 07 May 2014 03:41:46 -0300, Jochen Kemnade
<jochen.kemn...@eddyson.de> wrote:

Hi,

Hi!

I just re-discovered the ImmutableSessionPersistedObject annotation.
Maybe we can leverage that?

I think we could use both approaches, as we cannot add the annotation to
classes we don't own.


I'm still undecided how to handle this issue. To get started, I created and attached a simple patch that downgrades the write lock to a read lock if the attribute's class is a String, a primitive wrapper type or has the ImmutableSessionPersistedObject annotation. This is of course not very flexible but should work in a lot of situations. And we can always extend the list of immutable classes later. However, if we want a more flexible approach, I guess the best way to go would be a SessionPersistedObjectAnalyzer2 interface, with an added boolean isMutable(T sessionPersistedObject) method.
Any thoughts?

Jochen
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
index 7829285..dcf65f1 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry5.internal.services;
 
+import org.apache.tapestry5.annotations.ImmutableSessionPersistedObject;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.services.PerthreadManager;
@@ -21,9 +22,11 @@ import org.apache.tapestry5.services.Session;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
+
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A thin wrapper around {@link HttpSession}.
@@ -36,8 +39,15 @@ public class SessionImpl implements Session
 
     private boolean invalidated = false;
 
+    private boolean mutableObjectWasRead = false;
+
     private final SessionLock lock;
 
+    @SuppressWarnings("rawtypes")
+    private final static Set<Class> immutableClasses = Collections.unmodifiableSet(
+        CollectionFactory.<Class, Class>newSet(String.class, Character.class, Byte.class,
+            Integer.class, Long.class, Short.class, Double.class, Boolean.class));
+
     public SessionImpl(HttpServletRequest request, HttpSession session, SessionLock lock)
     {
         this.request = request;
@@ -47,9 +57,40 @@ public class SessionImpl implements Session
 
     public Object getAttribute(String name)
     {
+        // We acquire the write lock as a precaution because the object that is to be read might be mutable
         lock.acquireWriteLock();
 
-        return session.getAttribute(name);
+        Object attributeValue = session.getAttribute(name);
+
+        // If no mutable object was read from the session before, we may be able to release the write lock
+        if (!mutableObjectWasRead)
+        {
+            if (!isMutableObject(attributeValue))
+            {
+                lock.downgradeWriteLockToReadLock();
+            } else
+            {
+                mutableObjectWasRead = true;
+            }
+        }
+        return attributeValue;
+    }
+
+    private static boolean isMutableObject(Object object)
+    {
+        if (object == null){
+            return false;
+        }
+        Class<?> objectClass = object.getClass();
+        if (objectClass.getAnnotation(ImmutableSessionPersistedObject.class) != null)
+        {
+            return false;
+        }
+        if (immutableClasses.contains(objectClass))
+        {
+            return false;
+        }
+        return true;
     }
 
     public List<String> getAttributeNames()
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionLock.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionLock.java
index d234bd4..be1cdd9 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionLock.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionLock.java
@@ -33,4 +33,9 @@ public interface SessionLock
      * this may block for a while. Does nothing if the write lock is already held by this thread.
      */
     void acquireWriteLock();
+
+    /**
+     * Releases the exclusive write lock and acquires the read lock instead.
+     */
+    void downgradeWriteLockToReadLock();
 }
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TapestrySessionFactoryImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TapestrySessionFactoryImpl.java
index bc221a6..0faccfa 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TapestrySessionFactoryImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/TapestrySessionFactoryImpl.java
@@ -53,6 +53,10 @@ public class TapestrySessionFactoryImpl implements TapestrySessionFactory
         public void acquireWriteLock()
         {
         }
+
+        public void downgradeWriteLockToReadLock()
+        {
+        }
     };
 
     private class SessionLockImpl implements SessionLock
@@ -112,11 +116,24 @@ public class TapestrySessionFactoryImpl implements TapestrySessionFactory
             {
                 public void run()
                 {
-                    // This is the only way a write lock is unlocked, so no check is needed.
-                    lock.writeLock().unlock();
+                    // The write lock may have been released, if downgraded to a read lock.
+                    if (isWriteLocked())
+                    {
+                        lock.writeLock().unlock();
+                    }
                 }
             });
         }
+
+        @Override
+        public void downgradeWriteLockToReadLock()
+        {
+            lock.writeLock().unlock();
+
+            // During this window, no lock is held, and the next call may block.
+
+            acquireReadLock();
+        }
     }
 
     public TapestrySessionFactoryImpl(

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tapestry.apache.org
For additional commands, e-mail: dev-h...@tapestry.apache.org

Reply via email to