You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by jo...@apache.org on 2011/06/25 14:31:38 UTC
svn commit: r1139536 - in /tapestry/tapestry5/trunk/tapestry-core: ./
src/main/java/org/apache/tapestry5/
src/main/java/org/apache/tapestry5/internal/services/
src/main/java/org/apache/tapestry5/services/ src/test/cluster/
src/test/cluster/WEB-INF/ src...
Author: joshcanfield
Date: Sat Jun 25 12:31:36 2011
New Revision: 1139536
URL: http://svn.apache.org/viewvc?rev=1139536&view=rev
Log:
TAP-1489 - Created a SessionFactory which determines which implementation to provide to the request.
- Updated Session object to be able to report if it's been invalidated outside of tapestry
TAP-1355 - Removed code that set session attributes to null to force a HttpSessionBindingListener event.
- Created mini-cluster integration test to ensure proper session storage with different approaches.
- Changed isDirty flags to atomic checkAndSet style to avoid missed updates
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java
- copied, changed from r1139143, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/
tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/
tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml
- copied, changed from r1139143, tapestry/tapestry5/trunk/tapestry-core/src/test/app5/WEB-INF/web.xml
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml
Modified:
tapestry/tapestry5/trunk/tapestry-core/build.gradle
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java
Modified: tapestry/tapestry5/trunk/tapestry-core/build.gradle
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/build.gradle?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/build.gradle (original)
+++ tapestry/tapestry5/trunk/tapestry-core/build.gradle Sat Jun 25 12:31:36 2011
@@ -23,6 +23,8 @@ dependencies {
// Antlr3 tool path used with the antlr3 task
antlr3 "org.antlr:antlr:3.3"
+
+ testRuntime "org.hsqldb:hsqldb:1.8.0.10"
}
// This may spin out as a plugin once we've got the details down pat
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java Sat Jun 25 12:31:36 2011
@@ -1,4 +1,4 @@
-// Copyright 2008, 2009 The Apache Software Foundation
+// Copyright 2008, 2009, 2011 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,57 +14,33 @@
package org.apache.tapestry5;
-import javax.servlet.http.HttpSessionBindingEvent;
-import javax.servlet.http.HttpSessionBindingListener;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Base implementation of
* {@link org.apache.tapestry5.OptimizedSessionPersistedObject}. Subclasses
- * should invoke {@link #markDirty()} when internal state of the object changes.
+ * should invoke {@link #markDirty()} after the internal state of the object changes.
+ * <p>
+ * Due to the concurrent nature of session attributes it's important that markDirty occurs <strong>after</strong>
+ * the object has been changed. If the change occurs before the object has been mutated it's possible that another
+ * thread may re-store the object before the changes are actually made!
* <p>
- * Note that (due to TAP5-834), the object will receive a spurious
- * <code>valueUnbound()</code> notification when dirty. Tapestry sets dirty
- * session attributes to null, then to the persisted object, to force a
- * <code>valueBound()</code> notification, and that unfortunately also sends the
- * <code>valueUnbound()</code>.
- *
* @since 5.1.1.0
*/
-public abstract class BaseOptimizedSessionPersistedObject implements
- OptimizedSessionPersistedObject, HttpSessionBindingListener
+public abstract class BaseOptimizedSessionPersistedObject implements OptimizedSessionPersistedObject
{
- private transient boolean dirty;
-
- public final boolean isSessionPersistedObjectDirty()
- {
- return dirty;
- }
+ private transient AtomicBoolean dirty = new AtomicBoolean(false);
- /**
- * Invoked by the servlet container when the value is stored (or re-stored)
- * as an attribute of the session. This
- * clears the dirty flag. Subclasses may override this method, but should
- * invoke this implementation.
- */
- public void valueBound(HttpSessionBindingEvent event)
- {
- dirty = false;
- }
-
- /**
- * Does nothing.
- */
- public void valueUnbound(HttpSessionBindingEvent event)
+ public final boolean checkAndResetDirtyMarker()
{
+ return dirty.getAndSet(false);
}
/**
- * Invoked by the subclass whenever the internal state of the object
- * changes. Typically, this is invoked from
- * mutator methods.
+ * Invoked by the subclass after internal state of the object changes.
*/
protected final void markDirty()
{
- dirty = true;
+ dirty.set(true);
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java Sat Jun 25 12:31:36 2011
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 2011 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.
@@ -17,9 +17,11 @@ package org.apache.tapestry5;
/**
* An optional interface implemented by objects that are persisted in the {@link org.apache.tapestry5.services.Session}.
* At the end of each request, any objects read from the session are re-stored into the session, to ensure that
- * in-memory changes are flushed to other servers in a cluster. Objects that implement this interface are expected to
- * track when they are dirty (have pending changes), so that the save back into the session can be avoided when not
- * necessary.
+ * in-memory changes are flushed to other persistent session stores (e.g. RDBMS, servers in a cluster, etc). Objects
+ * that implement this interface are expected to track when they are dirty (have pending changes), so that the save
+ * back into the session can be avoided when not necessary.
+ * <p>
+ * This method is accessed concurrently.
*
* @see org.apache.tapestry5.annotations.ImmutableSessionPersistedObject
* @see org.apache.tapestry5.services.SessionPersistedObjectAnalyzer
@@ -28,10 +30,7 @@ package org.apache.tapestry5;
public interface OptimizedSessionPersistedObject
{
/**
- * Returns true if the object has in-memory changes. It is the object's responsibility to set its internal flag to
- * false, typically by implementing {@link javax.servlet.http.HttpSessionBindingListener}.
- *
- * @return
+ * @return true if the object has in-memory changes since the last time this method was called.
*/
- boolean isSessionPersistedObjectDirty();
+ boolean checkAndResetDirtyMarker();
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java Sat Jun 25 12:31:36 2011
@@ -294,4 +294,16 @@ public class SymbolConstants
* @since 5.3.0
*/
public static final String MINIFICATION_ENABLED = "tapestry.enable-minification";
+
+ /**
+ * If "true" then at the end of each request the
+ * {@link org.apache.tapestry5.services.SessionPersistedObjectAnalyzer} will be called on each session persisted
+ * object that was accessed during the request.
+ * <p>
+ * This is provided as a performance enhancement for servers that do not use clustered sessions.
+ *
+ * @since 5.3.1
+ */
+ public static final String CLUSTERED_SESSIONS = "tapestry.clustered-sessions";
+
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java Sat Jun 25 12:31:36 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2008, 2010 The Apache Software Foundation
+// Copyright 2006, 2008, 2010, 2011 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.
@@ -84,8 +84,6 @@ public final class ValidationTrackerImpl
private void store(FieldTracker fieldTracker)
{
- markDirty();
-
if (fieldTrackers == null)
fieldTrackers = CollectionFactory.newList();
@@ -98,15 +96,17 @@ public final class ValidationTrackerImpl
fieldTrackers.add(fieldTracker);
fieldToTracker.put(key, fieldTracker);
}
+
+ markDirty();
}
public void clear()
{
- markDirty();
-
extraErrors = null;
fieldTrackers = null;
fieldToTracker = null;
+
+ markDirty();
}
public String getError(Field field)
@@ -161,12 +161,12 @@ public final class ValidationTrackerImpl
public void recordError(String errorMessage)
{
- markDirty();
-
if (extraErrors == null)
extraErrors = CollectionFactory.newList();
extraErrors.add(errorMessage);
+
+ markDirty();
}
public void recordInput(Field field, String input)
Copied: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java (from r1139143, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java&r1=1139143&r2=1139536&rev=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java Sat Jun 25 12:31:36 2011
@@ -1,41 +1,33 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2011 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.
-// You may obtain a copy of the License at
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
//
-// http://www.apache.org/licenses/LICENSE-2.0
+// http://www.apache.org/licenses/LICENSE-2.0
//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.internal.util.InternalUtils;
-import org.apache.tapestry5.services.Session;
import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
+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.Map;
/**
- * A thin wrapper around {@link HttpSession}.
+ * A thin wrapper around {@link javax.servlet.http.HttpSession}.
*/
-public class SessionImpl implements Session
+public class ClusteredSessionImpl extends SessionImpl
{
private final SessionPersistedObjectAnalyzer analyzer;
- private final HttpSession session;
-
- private boolean invalidated = false;
-
/**
* Cache of attribute objects read from, or written to, the real session.
* This is needed for end-of-request
@@ -43,77 +35,42 @@ public class SessionImpl implements Sess
*/
private final Map<String, Object> sessionAttributeCache = CollectionFactory.newMap();
- public SessionImpl(HttpSession session, SessionPersistedObjectAnalyzer analyzer)
+ public ClusteredSessionImpl(
+ HttpServletRequest request,
+ HttpSession session,
+ SessionPersistedObjectAnalyzer analyzer)
{
- this.session = session;
+ super(request, session);
this.analyzer = analyzer;
}
+ @Override
public Object getAttribute(String name)
{
- Object result = session.getAttribute(name);
+ Object result = super.getAttribute(name);
sessionAttributeCache.put(name, result);
return result;
}
- public List<String> getAttributeNames()
- {
- return InternalUtils.toList(session.getAttributeNames());
- }
-
public void setAttribute(String name, Object value)
{
- session.setAttribute(name, value);
+ super.setAttribute(name, value);
sessionAttributeCache.put(name, value);
}
- public List<String> getAttributeNames(String prefix)
- {
- List<String> result = CollectionFactory.newList();
-
- Enumeration e = session.getAttributeNames();
- while (e.hasMoreElements())
- {
- String name = (String) e.nextElement();
-
- if (name.startsWith(prefix)) result.add(name);
- }
-
- Collections.sort(result);
-
- return result;
- }
-
- public int getMaxInactiveInterval()
- {
- return session.getMaxInactiveInterval();
- }
-
public void invalidate()
{
- invalidated = true;
-
- session.invalidate();
+ super.invalidate();
sessionAttributeCache.clear();
}
- public boolean isInvalidated()
- {
- return invalidated;
- }
-
- public void setMaxInactiveInterval(int seconds)
- {
- session.setMaxInactiveInterval(seconds);
- }
-
public void restoreDirtyObjects()
{
- if (invalidated) return;
+ if (isInvalidated()) return;
if (sessionAttributeCache.isEmpty()) return;
@@ -125,15 +82,9 @@ public class SessionImpl implements Sess
if (attributeValue == null) continue;
- if (analyzer.isDirty(attributeValue))
+ if (analyzer.checkAndResetDirtyState(attributeValue))
{
- // TAP5-834: Jetty & Tomcat work by object identity, will not update the attribute
- // and fire the session binding event unless there's a real change. So we set the
- // attribute to null and then to the new value and that should force the necessary
- // notification.
-
- session.setAttribute(attributeName, null);
- session.setAttribute(attributeName, attributeValue);
+ super.setAttribute(attributeName, attributeValue);
}
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java Sat Jun 25 12:31:36 2011
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 2011 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.
@@ -28,14 +28,14 @@ public class DefaultSessionPersistedObje
* An object is dirty <em>unless</em> it has the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject}
* annotation.
*
- * @param object to analyze
+ * @param sessionPersistedObject to analyze
* @return false if immutable, true otherwise
*/
- public boolean isDirty(Object object)
+ public boolean checkAndResetDirtyState(Object sessionPersistedObject)
{
- boolean immutable = object.getClass().getAnnotation(ImmutableSessionPersistedObject.class) != null;
+ boolean immutable = sessionPersistedObject.getClass().getAnnotation(ImmutableSessionPersistedObject.class) != null;
- // Imuutable objects are always clean, others are assumed dirty.
+ // Immutable objects are always clean, others are assumed dirty.
// Go implement OptimizedSessionPersistedObject if you don't like it.
return !immutable;
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java Sat Jun 25 12:31:36 2011
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 2011 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.
@@ -19,8 +19,8 @@ import org.apache.tapestry5.services.Ses
public class OptimizedSessionPersistedObjectAnalyzer implements SessionPersistedObjectAnalyzer<OptimizedSessionPersistedObject>
{
- public boolean isDirty(OptimizedSessionPersistedObject object)
+ public boolean checkAndResetDirtyState(OptimizedSessionPersistedObject sessionPersistedObject)
{
- return object.isSessionPersistedObjectDirty();
+ return sessionPersistedObject.checkAndResetDirtyMarker();
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java Sat Jun 25 12:31:36 2011
@@ -17,10 +17,8 @@ package org.apache.tapestry5.internal.se
import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import org.apache.tapestry5.services.Request;
import org.apache.tapestry5.services.Session;
-import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Locale;
@@ -39,19 +37,20 @@ public class RequestImpl implements Requ
private final String requestEncoding;
- private final SessionPersistedObjectAnalyzer analyzer;
+ private final SessionFactory sessionFactory;
private boolean encodingSet;
- HttpSession hsession;
-
Session session;
- public RequestImpl(HttpServletRequest request, String requestEncoding, SessionPersistedObjectAnalyzer analyzer)
+ public RequestImpl(
+ HttpServletRequest request,
+ String requestEncoding,
+ SessionFactory sessionFactory)
{
this.request = request;
this.requestEncoding = requestEncoding;
- this.analyzer = analyzer;
+ this.sessionFactory = sessionFactory;
}
public List<String> getParameterNames()
@@ -105,27 +104,15 @@ public class RequestImpl implements Requ
public Session getSession(boolean create)
{
- if (session != null)
+ if (session != null && session.isInvalidated())
{
- // The easy case is when the session was invalidated through the Tapestry Session
- // object. The hard case is when the HttpSession was invalidated outside of Tapestry,
- // in which case, request.getSession() will return a new HttpSession instance (or null)
-
- if (session.isInvalidated() || hsession != request.getSession(false))
- {
- session = null;
- hsession = null;
- }
+ session = null;
}
- if (session == null)
+ if (session == null )
{
- hsession = request.getSession(create);
-
- if (hsession != null)
- {
- session = new SessionImpl(hsession, analyzer);
- }
+ // TAP5-1489 - Re-storage of session attributes at end of request should be configurable
+ session = sessionFactory.getSession(create);
}
return session;
@@ -149,8 +136,7 @@ public class RequestImpl implements Requ
try
{
request.setCharacterEncoding(requestEncoding);
- }
- catch (UnsupportedEncodingException ex)
+ } catch (UnsupportedEncodingException ex)
{
throw new RuntimeException(ex);
}
@@ -198,7 +184,9 @@ public class RequestImpl implements Requ
return request.getLocalPort();
}
- /** @since 5.2.5 */
+ /**
+ * @since 5.2.5
+ */
public int getServerPort()
{
return request.getServerPort();
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,22 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.services;
+
+import org.apache.tapestry5.services.Session;
+
+public interface SessionFactory
+{
+ Session getSession(boolean create);
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,59 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.services;
+
+import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.services.Session;
+import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+/**
+ * User: josh_canfield
+ * Date: 6/25/11
+ */
+public class SessionFactoryImpl implements SessionFactory
+{
+ private boolean clustered;
+ private final SessionPersistedObjectAnalyzer analyzer;
+ private final HttpServletRequest request;
+
+ public SessionFactoryImpl(
+ @Symbol(SymbolConstants.CLUSTERED_SESSIONS)
+ boolean clustered,
+ SessionPersistedObjectAnalyzer analyzer,
+ HttpServletRequest request)
+ {
+ this.clustered = clustered;
+ this.analyzer = analyzer;
+ this.request = request;
+ }
+
+ public Session getSession(boolean create)
+ {
+ final HttpSession httpSession = request.getSession(create);
+
+ if (httpSession == null ) return null;
+
+ if (clustered)
+ {
+ return new ClusteredSessionImpl(request, httpSession, analyzer);
+ }
+
+ return new SessionImpl(request, httpSession);
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java Sat Jun 25 12:31:36 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2011 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.
@@ -19,6 +19,7 @@ import org.apache.tapestry5.ioc.internal
import org.apache.tapestry5.services.Session;
import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Collections;
import java.util.Enumeration;
@@ -30,32 +31,20 @@ import java.util.Map;
*/
public class SessionImpl implements Session
{
- private final SessionPersistedObjectAnalyzer analyzer;
-
+ private final HttpServletRequest request;
private final HttpSession session;
private boolean invalidated = false;
- /**
- * Cache of attribute objects read from, or written to, the real session.
- * This is needed for end-of-request
- * processing.
- */
- private final Map<String, Object> sessionAttributeCache = CollectionFactory.newMap();
-
- public SessionImpl(HttpSession session, SessionPersistedObjectAnalyzer analyzer)
+ public SessionImpl(HttpServletRequest request, HttpSession session)
{
+ this.request = request;
this.session = session;
- this.analyzer = analyzer;
}
public Object getAttribute(String name)
{
- Object result = session.getAttribute(name);
-
- sessionAttributeCache.put(name, result);
-
- return result;
+ return session.getAttribute(name);
}
public List<String> getAttributeNames()
@@ -66,8 +55,6 @@ public class SessionImpl implements Sess
public void setAttribute(String name, Object value)
{
session.setAttribute(name, value);
-
- sessionAttributeCache.put(name, value);
}
public List<String> getAttributeNames(String prefix)
@@ -97,12 +84,18 @@ public class SessionImpl implements Sess
invalidated = true;
session.invalidate();
-
- sessionAttributeCache.clear();
}
public boolean isInvalidated()
{
+ if (invalidated) return true;
+
+ // The easy case is when the session was invalidated through the Tapestry Session
+ // object. The hard case is when the HttpSession was invalidated outside of Tapestry,
+ // in which case, request.getSession() will return a new HttpSession instance (or null)
+
+ invalidated = request.getSession(false) != session;
+
return invalidated;
}
@@ -113,28 +106,6 @@ public class SessionImpl implements Sess
public void restoreDirtyObjects()
{
- if (invalidated) return;
-
- if (sessionAttributeCache.isEmpty()) return;
-
- for (Map.Entry<String, Object> entry : sessionAttributeCache.entrySet())
- {
- String attributeName = entry.getKey();
-
- Object attributeValue = entry.getValue();
- if (attributeValue == null) continue;
-
- if (analyzer.isDirty(attributeValue))
- {
- // TAP5-834: Jetty & Tomcat work by object identity, will not update the attribute
- // and fire the session binding event unless there's a real change. So we set the
- // attribute to null and then to the new value and that should force the necessary
- // notification.
-
- session.setAttribute(attributeName, null);
- session.setAttribute(attributeName, attributeValue);
- }
- }
}
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java Sat Jun 25 12:31:36 2011
@@ -72,8 +72,8 @@ public interface Session
void invalidate();
/**
- * Checks to see if the session has been invalidated. Note: this only catches calls to {@link #invalidate()}, not
- * calls to {@link javax.servlet.http.HttpSession#invalidate()}.
+ * Checks to see if the session has been invalidated. Note: since 5.3.1 this will also catch calls to
+ * {@link javax.servlet.http.HttpSession#invalidate()}.
*
* @since 5.1.0.0
*/
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java Sat Jun 25 12:31:36 2011
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 2011 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.
@@ -17,10 +17,15 @@ package org.apache.tapestry5.services;
import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
/**
- * Analyzes a session-persisted object, specifically to see if it is dirty or not. The service implementation uses a
- * mapped configuration to form a {@linkplain org.apache.tapestry5.ioc.services.StrategyBuilder strategy} based on
- * object type. The service is injectable using the {@link org.apache.tapestry5.ioc.annotations.Primary} marker
- * annotation.
+ * Analyzes a session-persisted object, specifically to see if it is dirty or not.
+ * <p/>
+ * This service is provided to support applications which store mutable session attributes where the
+ * session is replicated to a slower medium (e.g. RDMBS, Cluster, etc) this can help alleviate excessive writes
+ * to the session store while ensuring changes are propagated.
+ * <p/>
+ * The service implementation uses a mapped configuration to form a
+ * {@linkplain org.apache.tapestry5.ioc.services.StrategyBuilder strategy} based on object type. The service may be
+ * injected using the {@link org.apache.tapestry5.ioc.annotations.Primary} marker annotation.
*
* @see org.apache.tapestry5.annotations.ImmutableSessionPersistedObject
* @see org.apache.tapestry5.OptimizedSessionPersistedObject
@@ -30,11 +35,15 @@ import org.apache.tapestry5.ioc.annotati
public interface SessionPersistedObjectAnalyzer<T>
{
/**
- * Passed an object (never null) to see if it is dirty or not. Dirty objects that are stored in the session are
- * re-stored into the session at the end of the request.
+ * Atomically check and reset the dirty state of the session persisted object.
+ * <p/>
+ * The implementer should take consideration for the fact that session attributes are accessed concurrently. A
+ * naive check/set algorithm may allow changes to go un-noticed.
*
- * @param object
- * @return true if object needs to be re-stored into the session
+ * @param sessionPersistedObject the session attribute (never null)
+ * @return true if the object needs to be re-stored into the session
+ * @since 5.3.1
*/
- boolean isDirty(T object);
+ boolean checkAndResetDirtyState(T sessionPersistedObject);
+
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Sat Jun 25 12:31:36 2011
@@ -355,14 +355,14 @@ public final class TapestryModule
{
private final RequestHandler handler;
private final String applicationCharset;
- private final SessionPersistedObjectAnalyzer analyzer;
+ private final SessionFactory sessionFactory;
public HttpServletRequestHandlerTerminator(RequestHandler handler, String applicationCharset,
- SessionPersistedObjectAnalyzer analyzer)
+ SessionFactory sessionFactory)
{
this.handler = handler;
this.applicationCharset = applicationCharset;
- this.analyzer = analyzer;
+ this.sessionFactory = sessionFactory;
}
public boolean service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
@@ -370,7 +370,7 @@ public final class TapestryModule
{
requestGlobals.storeServletRequestResponse(servletRequest, servletResponse);
- Request request = new RequestImpl(servletRequest, applicationCharset, analyzer);
+ Request request = new RequestImpl(servletRequest, applicationCharset, sessionFactory);
Response response = new ResponseImpl(servletRequest, servletResponse);
// TAP5-257: Make sure that the "initial guess" for request/response
@@ -484,6 +484,7 @@ public final class TapestryModule
binder.bind(ContextPathEncoder.class, ContextPathEncoderImpl.class);
binder.bind(ApplicationStatePersistenceStrategy.class, SessionApplicationStatePersistenceStrategy.class)
.withId("SessionApplicationStatePersistenceStrategy");
+ binder.bind(SessionFactory.class, SessionFactoryImpl.class);
binder.bind(AssetPathConverter.class, IdentityAssetPathConverter.class);
binder.bind(NumericTranslatorSupport.class);
binder.bind(ClientDataEncoder.class, ClientDataEncoderImpl.class);
@@ -1115,7 +1116,7 @@ public final class TapestryModule
* Adds coercions:
* <ul>
* <li>String to {@link SelectModel}
- * <li>Map to {@link oSelectModel}
+ * <li>Map to {@link SelectModel}
* <li>Collection to {@link GridDataSource}
* <li>null to {@link GridDataSource}
* <li>List to {@link SelectModel}
@@ -1516,11 +1517,10 @@ public final class TapestryModule
@Symbol(SymbolConstants.CHARSET)
String applicationCharset,
- @Primary
- SessionPersistedObjectAnalyzer analyzer)
+ SessionFactory sessionFactory)
{
HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset,
- analyzer);
+ sessionFactory);
return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class,
configuration, terminator);
@@ -2457,6 +2457,8 @@ public final class TapestryModule
configuration.add(SymbolConstants.PRODUCTION_MODE, true);
+ configuration.add(SymbolConstants.CLUSTERED_SESSIONS, true);
+
configuration.add(SymbolConstants.COMPRESS_WHITESPACE, true);
configuration.add(MetaDataConstants.SECURE_PAGE, false);
@@ -2704,7 +2706,7 @@ public final class TapestryModule
}
/**
- * The master SessionPesistedObjectAnalyzer.
+ * The master SessionPersistedObjectAnalyzer.
*
* @since 5.1.0.0
*/
@@ -2730,7 +2732,7 @@ public final class TapestryModule
SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
{
- public boolean isDirty(Object object)
+ public boolean checkAndResetDirtyState(Object sessionPersistedObject)
{
return false;
}
Copied: tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml (from r1139143, tapestry/tapestry5/trunk/tapestry-core/src/test/app5/WEB-INF/web.xml)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml?p2=tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml&p1=tapestry/tapestry5/trunk/tapestry-core/src/test/app5/WEB-INF/web.xml&r1=1139143&r2=1139536&rev=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app5/WEB-INF/web.xml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml Sat Jun 25 12:31:36 2011
@@ -3,10 +3,10 @@
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
- <display-name>Integration Test App 5 -- Skinning</display-name>
+ <display-name>Integration Test Cluster</display-name>
<context-param>
<param-name>tapestry.app-package</param-name>
- <param-value>org.apache.tapestry5.integration.app5</param-value>
+ <param-value>org.apache.tapestry5.integration.cluster</param-value>
</context-param>
<filter>
<filter-name>app</filter-name>
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,248 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster;
+
+import com.thoughtworks.selenium.DefaultSelenium;
+import com.thoughtworks.selenium.Selenium;
+import org.apache.tapestry5.test.Jetty7Runner;
+import org.apache.tapestry5.test.TapestryTestConstants;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.session.JDBCSessionIdManager;
+import org.eclipse.jetty.server.session.JDBCSessionManager;
+import org.eclipse.jetty.server.session.SessionHandler;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.openqa.selenium.server.RemoteControlConfiguration;
+import org.openqa.selenium.server.SeleniumServer;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.testng.xml.XmlTest;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+/**
+ * User: josh_canfield Date: 6/24/11
+ */
+public class ClusterTests
+{
+ private static final String FIREFOX_BROWSER_CMD = "*firefox";
+ private static final int SERVER_A_PORT = 9091;
+ private static final int SERVER_B_PORT = 9092;
+ private static final String SERVER_A_NAME = "server_A";
+ private static final String SERVER_B_NAME = "server_B";
+
+ private static final String CREATE_1 = "//a[contains(text(),'create1')]";
+ private static final String VALUE_1 = "value1";
+
+ private static final String CREATE_2 = "//a[contains(text(),'create2')]";
+ private static final String VALUE_2 = "value2";
+
+ private static final String UPDATE_1 = "//a[contains(text(),'update1')]";
+ private static final String VALUE_3 = "value3";
+
+ private static final String UPDATE_2 = "//a[contains(text(),'update2')]";
+ private static final String VALUE_4 = "value4";
+
+ private static final String CLEAR = "//a[contains(text(),'Clear')]";
+
+ Jetty7Runner serverA;
+
+ Jetty7Runner serverB;
+
+ SeleniumServer seleniumServer;
+ Selenium selenium;
+
+ @BeforeClass
+ void setupServers(XmlTest xmlTest) throws Exception
+ {
+ createJettySessionsTable();
+
+ serverA = configureClusteredJetty(SERVER_A_NAME, SERVER_A_PORT);
+ serverB = configureClusteredJetty(SERVER_B_NAME, SERVER_B_PORT);
+
+ seleniumServer = new SeleniumServer();
+ seleniumServer.start();
+
+ String browserStartCommand = xmlTest.getParameter(TapestryTestConstants.BROWSER_START_COMMAND_PARAMETER);
+ browserStartCommand = browserStartCommand != null ? browserStartCommand : FIREFOX_BROWSER_CMD;
+
+ selenium = new DefaultSelenium(
+ "localhost", RemoteControlConfiguration.DEFAULT_PORT,
+ browserStartCommand, "http://localhost:9091/"
+ );
+ selenium.start();
+ }
+
+ @AfterClass
+ void stopServers()
+ {
+ serverA.stop();
+ serverB.stop();
+ selenium.stop();
+ seleniumServer.stop();
+ }
+
+ @Test
+ public void mutable_pojo_as_session_state_is_always_shared()
+ {
+ // Expect all object changes to be transferred to the other server.
+ String[][] click = {
+ {CREATE_1, VALUE_1, VALUE_1},
+ {CLEAR, "", ""},
+ {CREATE_2, VALUE_2, VALUE_2},
+ {UPDATE_1, VALUE_3, VALUE_3},
+ {UPDATE_2, VALUE_4, VALUE_4}
+ };
+
+ evaluate("PersistedMutablePojoDemo", click);
+ }
+
+ @Test
+ public void immutable_session_persisted_object() throws InterruptedException
+ {
+ // expect only create links to transfer over (We've told tapestry it's immutable)
+ String[][] data = {
+ {CREATE_1, VALUE_1, VALUE_1},
+ {UPDATE_1, VALUE_3, VALUE_1},
+ {CLEAR, "", ""},
+ {CREATE_2, VALUE_2, VALUE_2},
+ {UPDATE_2, VALUE_4, VALUE_2},
+ {UPDATE_1, VALUE_3, VALUE_2},
+ };
+
+ evaluate("ImmutableSessionPersistedObjectDemo", data);
+ }
+
+ @Test
+ public void session_persisted_object_analyzer() throws InterruptedException
+ {
+ // special cased so that only UPDATE_2 marks as dirty, creates/deletes still transfer
+ String[][] data = {
+ {CREATE_1, VALUE_1, VALUE_1}, // created, session transferred
+ {UPDATE_1, VALUE_3, VALUE_1}, // update-1 doesn't transfer
+ {CLEAR, "", ""},
+ {CREATE_2, VALUE_2, VALUE_2}, // create-2, session transferred
+ {UPDATE_2, VALUE_4, VALUE_4}, // update-2, session transferred
+ {UPDATE_1, VALUE_3, VALUE_4}, // update-1, doesn't transfer
+ };
+
+ evaluate("SessionPersistedObjectAnalyzerDemo", data);
+ }
+
+ private void evaluate(String page, String[][] expect)
+ {
+ for (String[] strings : expect)
+ {
+ openOnServerA(page);
+
+ clickAndWait(strings[0]);
+
+ assertText("value", strings[1]);
+
+ openOnServerB(page);
+
+ assertText("value", strings[2]);
+ }
+
+ clickAndWait(CLEAR);
+ assertText("value", "");
+ }
+
+ private void openOnServerA(String page)
+ {
+ selenium.open("http://localhost:" + SERVER_A_PORT + "/" + page);
+ assertServerName(SERVER_A_NAME);
+ }
+
+ private void openOnServerB(String page)
+ {
+ selenium.open("http://localhost:" + SERVER_B_PORT + "/" + page);
+ assertServerName(SERVER_B_NAME);
+ }
+
+ private void assertServerName(String serverName)
+ {
+ assertTrue(selenium.isElementPresent("//h1[@id='serverName' and text()='" + serverName + "']"));
+ }
+
+ private void assertText(String locator, String expected)
+ {
+ assertEquals(expected, selenium.getText(locator));
+ }
+
+ private void clickAndWait(String s)
+ {
+ selenium.click(s);
+ selenium.waitForPageToLoad("5000");
+ }
+
+ private Jetty7Runner configureClusteredJetty(String name, int port) throws Exception
+ {
+ Jetty7Runner runner = new Jetty7Runner();
+
+ runner.configure("src/test/cluster", "", port, port + 100);
+
+ JDBCSessionIdManager idMgr = new JDBCSessionIdManager(runner.getServer());
+ idMgr.setWorkerName(name);
+ idMgr.setDriverInfo("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:clustertest");
+
+ Server server = runner.getServer();
+ server.setSessionIdManager(idMgr);
+
+ WebAppContext wac = (WebAppContext) server.getHandler();
+
+ JDBCSessionManager jdbcMgr = new JDBCSessionManager();
+ jdbcMgr.setIdManager(server.getSessionIdManager());
+
+ // force the session to be read from the database with no delay
+ // This is an incorrectly documented feature.
+ jdbcMgr.setSaveInterval(0);
+
+ wac.setSessionHandler(new SessionHandler(jdbcMgr));
+ wac.getServletContext().setInitParameter("cluster.name", name);
+ runner.start();
+ return runner;
+ }
+
+ private void createJettySessionsTable() throws ClassNotFoundException, SQLException
+ {
+ Class.forName("org.hsqldb.jdbcDriver");
+
+ Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:clustertest", "sa", "");
+ String sql = "create table JettySessions (" +
+ "rowId varchar(60), " +
+ "sessionId varchar(60)," +
+ "contextPath varchar(60)," +
+ "virtualHost varchar(60)," +
+ "lastNode varchar(60)," +
+ "accessTime bigint," +
+ "lastAccessTime bigint," +
+ "createTime bigint," +
+ "cookieTime bigint," +
+ "lastSavedTime bigint," +
+ "expiryTime bigint," +
+ "map longvarbinary" +
+ ");";
+ Statement statement = c.createStatement();
+ statement.execute(sql);
+ statement.close();
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,41 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster.base;
+
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.integration.cluster.data.SessionStateObject;
+
+public abstract class BaseSessionDemo<T extends SessionStateObject> {
+
+ @Property
+ @Persist
+ private SessionStateObject data;
+
+ abstract public T create(String value);
+
+ void onCreateValue(String value) {
+ data = create(value);
+ }
+
+ void onChangeValue(String value) {
+ data.setValue(value);
+ }
+
+ void onClear() {
+ data = null;
+ }
+
+}
\ No newline at end of file
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,45 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster.data;
+
+public class AnalyzedSessionObject implements SessionStateObject
+{
+ private String value;
+
+ boolean dirty = false;
+
+ public AnalyzedSessionObject(String value)
+ {
+ this.value = value;
+ }
+
+ public boolean checkAndResetDirtyState()
+ {
+ boolean check = dirty;
+ dirty = false;
+ return check;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+
+ public void setValue(String value)
+ {
+ this.value = value;
+ dirty = "value4".equals(value);
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,34 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster.data;
+
+import org.apache.tapestry5.annotations.ImmutableSessionPersistedObject;
+
+@ImmutableSessionPersistedObject
+public class ImmutableByAnnotation implements SessionStateObject {
+ private String value;
+
+ public ImmutableByAnnotation(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,32 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster.data;
+
+public class MutablePojo implements SessionStateObject {
+
+ private String value;
+
+ public MutablePojo(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String name) {
+ this.value = name;
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,24 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster.data;
+
+import java.io.Serializable;
+
+public interface SessionStateObject extends Serializable {
+
+ String getValue();
+
+ void setValue(String v);
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,29 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster.pages;
+
+import org.apache.tapestry5.integration.cluster.base.BaseSessionDemo;
+import org.apache.tapestry5.integration.cluster.data.ImmutableByAnnotation;
+
+/**
+ * User: josh_canfield
+ * Date: 6/24/11
+ */
+public class ImmutableSessionPersistedObjectDemo extends BaseSessionDemo<ImmutableByAnnotation> {
+ @Override
+ public ImmutableByAnnotation create(String value) {
+ return new ImmutableByAnnotation(value);
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,26 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster.pages;
+
+import org.apache.tapestry5.integration.cluster.base.BaseSessionDemo;
+import org.apache.tapestry5.integration.cluster.data.MutablePojo;
+
+public class PersistedMutablePojoDemo extends BaseSessionDemo<MutablePojo> {
+
+ @Override
+ public MutablePojo create(String value) {
+ return new MutablePojo(value);
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,25 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster.pages;
+
+import org.apache.tapestry5.integration.cluster.base.BaseSessionDemo;
+import org.apache.tapestry5.integration.cluster.data.AnalyzedSessionObject;
+
+public class SessionPersistedObjectAnalyzerDemo extends BaseSessionDemo<AnalyzedSessionObject> {
+ @Override
+ public AnalyzedSessionObject create(String value) {
+ return new AnalyzedSessionObject(value);
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java Sat Jun 25 12:31:36 2011
@@ -0,0 +1,36 @@
+// Copyright 2011 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.cluster.services;
+
+import org.apache.tapestry5.integration.cluster.data.AnalyzedSessionObject;
+import org.apache.tapestry5.ioc.MappedConfiguration;
+import org.apache.tapestry5.ioc.annotations.Contribute;
+import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
+
+public class AppModule
+{
+
+ @Contribute(SessionPersistedObjectAnalyzer.class)
+ public static void analyzer(MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
+ {
+ configuration.add(AnalyzedSessionObject.class, new SessionPersistedObjectAnalyzer<AnalyzedSessionObject>()
+ {
+ public boolean checkAndResetDirtyState(AnalyzedSessionObject sessionPersistedObject)
+ {
+ return sessionPersistedObject.checkAndResetDirtyState();
+ }
+ });
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java Sat Jun 25 12:31:36 2011
@@ -33,12 +33,13 @@ public class RequestImplTest extends Int
public void get_session_doesnt_exist()
{
HttpServletRequest sr = mockHttpServletRequest();
+ SessionFactory sf = newMock(SessionFactory.class);
- train_getSession(sr, false, null);
+ expect(sf.getSession(false)).andReturn(null);
replay();
- Request request = new RequestImpl(sr, CHARSET, null);
+ Request request = new RequestImpl(sr, CHARSET, sf);
assertNull(request.getSession(false));
@@ -50,14 +51,15 @@ public class RequestImplTest extends Int
{
HttpServletRequest sr = mockHttpServletRequest();
HttpSession ss = mockHttpSession();
+ SessionFactory sf = newMock(SessionFactory.class);
- train_getSession(sr, true, ss);
+ expect(sf.getSession(true)).andReturn(new SessionImpl(sr, ss));
train_getAttribute(ss, "foo", "bar");
replay();
- Request request = new RequestImpl(sr, CHARSET, null);
+ Request request = new RequestImpl(sr, CHARSET, sf);
Session session = request.getSession(true);
assertEquals(session.getAttribute("foo"), "bar");
@@ -202,11 +204,13 @@ public class RequestImplTest extends Int
HttpSession hsession1 = mockHttpSession();
HttpSession hsession2 = mockHttpSession();
- train_getSession(sr, true, hsession1);
+ SessionFactory sf = newMock(SessionFactory.class);
+
+ expect(sf.getSession(true)).andReturn(new SessionImpl(sr,hsession1));
replay();
- Request request = new RequestImpl(sr, CHARSET, null);
+ Request request = new RequestImpl(sr, CHARSET, sf);
Session session1 = request.getSession(true);
@@ -214,8 +218,11 @@ public class RequestImplTest extends Int
hsession1.invalidate();
- train_getSession(sr, false, hsession2);
- train_getSession(sr, true, hsession2);
+ expect(sr.getSession(false)).andReturn(null);
+
+ SessionImpl session = new SessionImpl(sr, hsession2);
+ expect(sf.getSession(true)).andReturn(session);
+ expect(sf.getSession(true)).andReturn(session);
replay();
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java?rev=1139536&r1=1139535&r2=1139536&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java Sat Jun 25 12:31:36 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2011 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.
@@ -18,6 +18,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
+import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.tapestry5.internal.test.InternalBaseTestCase;
@@ -37,7 +38,7 @@ public class SessionImplTest extends Int
replay();
- Session session = new SessionImpl(hs, null);
+ Session session = new SessionImpl(null, hs);
assertEquals(session.getAttributeNames(), Arrays.asList("barney", "fred"));
@@ -54,7 +55,7 @@ public class SessionImplTest extends Int
replay();
- Session session = new SessionImpl(hs, null);
+ Session session = new SessionImpl(null, hs);
assertEquals(session.getAttributeNames("f"), Arrays.asList("fanny", "fred"));
@@ -70,7 +71,7 @@ public class SessionImplTest extends Int
replay();
- Session session = new SessionImpl(hs, null);
+ Session session = new SessionImpl(null, hs);
session.invalidate();
@@ -78,6 +79,39 @@ public class SessionImplTest extends Int
}
@Test
+ public void http_session_invalidate()
+ {
+ HttpSession hs = mockHttpSession();
+
+ HttpServletRequest hsr = mockHttpServletRequest();
+
+ train_getSession(hsr, false, hs);
+
+ replay();
+
+ Session session = new SessionImpl(hsr, hs);
+
+ assertFalse(session.isInvalidated());
+
+ verify();
+
+ train_getSession(hsr, false, null);
+
+ replay();
+
+ assertTrue(session.isInvalidated());
+
+ verify();
+
+ train_getSession(hsr, false, mockHttpSession());
+
+ replay();
+
+ assertTrue(session.isInvalidated());
+
+ }
+
+ @Test
public void set_max_inactive()
{
HttpSession hs = mockHttpSession();
@@ -87,7 +121,7 @@ public class SessionImplTest extends Int
replay();
- Session session = new SessionImpl(hs, null);
+ Session session = new SessionImpl(null, hs);
session.setMaxInactiveInterval(seconds);
@@ -104,7 +138,7 @@ public class SessionImplTest extends Int
replay();
- Session session = new SessionImpl(hs, null);
+ Session session = new SessionImpl(null, hs);
assertEquals(session.getMaxInactiveInterval(), seconds);
@@ -115,6 +149,7 @@ public class SessionImplTest extends Int
public void dirty_persisted_object_is_forced_to_update()
{
HttpSession hs = mockHttpSession();
+ HttpServletRequest hsr = mockHttpServletRequest();
SessionPersistedObjectAnalyzer analyzer = newMock(SessionPersistedObjectAnalyzer.class);
Object dirty = new Object();
@@ -122,15 +157,16 @@ public class SessionImplTest extends Int
replay();
- Session session = new SessionImpl(hs, analyzer);
+ Session session = new ClusteredSessionImpl(hsr, hs, analyzer);
assertSame(session.getAttribute("dirty"), dirty);
verify();
- expect(analyzer.isDirty(dirty)).andReturn(true);
+ expect(analyzer.checkAndResetDirtyState(dirty)).andReturn(true);
+
+ train_getSession(hsr, false, hs);
- hs.setAttribute("dirty", null);
hs.setAttribute("dirty", dirty);
replay();
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml?rev=1139536&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml Sat Jun 25 12:31:36 2011
@@ -0,0 +1,21 @@
+<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
+<head>
+ <title>Immutable Annotation Demo</title>
+</head>
+
+<body>
+<h1 id="serverName">${symbol:cluster.name}</h1>
+
+<div id="value">${data?.value}</div>
+
+<t:eventlink event="clear">Clear</t:eventlink>
+
+<t:eventlink event="createValue" context="'value1'">create1</t:eventlink>
+<t:eventlink event="createValue" context="'value2'">create2</t:eventlink>
+
+<t:eventlink event="changeValue" context="'value3'">update1</t:eventlink>
+<t:eventlink event="changeValue" context="'value4'">update2</t:eventlink>
+
+</body>
+
+</html>
\ No newline at end of file
Re: svn commit: r1139536 - in /tapestry/tapestry5/trunk/tapestry-core:
./ src/main/java/org/apache/tapestry5/ src/main/java/org/apache/tapestry5/internal/services/
src/main/java/org/apache/tapestry5/services/ src/test/cluster/ src/test/cluster/WEB-INF/
src...
Posted by Ulrich Stärk <ul...@spielviel.de>.
Nice one. In the future please make sure to correctly state the issue number (you omitted the 5
after TAP) so that we have them linked from JIRA. This makes finding the corresponding code changes
for an issue a lot easier!
Uli
On 25.06.2011 14:31, joshcanfield@apache.org wrote:
> Author: joshcanfield
> Date: Sat Jun 25 12:31:36 2011
> New Revision: 1139536
>
> URL: http://svn.apache.org/viewvc?rev=1139536&view=rev
> Log:
> TAP-1489 - Created a SessionFactory which determines which implementation to provide to the request.
> - Updated Session object to be able to report if it's been invalidated outside of tapestry
>
> TAP-1355 - Removed code that set session attributes to null to force a HttpSessionBindingListener event.
> - Created mini-cluster integration test to ensure proper session storage with different approaches.
> - Changed isDirty flags to atomic checkAndSet style to avoid missed updates
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java
> - copied, changed from r1139143, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/
> tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/
> tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml
> - copied, changed from r1139143, tapestry/tapestry5/trunk/tapestry-core/src/test/app5/WEB-INF/web.xml
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/
> tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/
> tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml
> Modified:
> tapestry/tapestry5/trunk/tapestry-core/build.gradle
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/build.gradle
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/build.gradle?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/build.gradle (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/build.gradle Sat Jun 25 12:31:36 2011
> @@ -23,6 +23,8 @@ dependencies {
>
> // Antlr3 tool path used with the antlr3 task
> antlr3 "org.antlr:antlr:3.3"
> +
> + testRuntime "org.hsqldb:hsqldb:1.8.0.10"
> }
>
> // This may spin out as a plugin once we've got the details down pat
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/BaseOptimizedSessionPersistedObject.java Sat Jun 25 12:31:36 2011
> @@ -1,4 +1,4 @@
> -// Copyright 2008, 2009 The Apache Software Foundation
> +// Copyright 2008, 2009, 2011 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,57 +14,33 @@
>
> package org.apache.tapestry5;
>
> -import javax.servlet.http.HttpSessionBindingEvent;
> -import javax.servlet.http.HttpSessionBindingListener;
> +import java.util.concurrent.atomic.AtomicBoolean;
>
> /**
> * Base implementation of
> * {@link org.apache.tapestry5.OptimizedSessionPersistedObject}. Subclasses
> - * should invoke {@link #markDirty()} when internal state of the object changes.
> + * should invoke {@link #markDirty()} after the internal state of the object changes.
> + * <p>
> + * Due to the concurrent nature of session attributes it's important that markDirty occurs <strong>after</strong>
> + * the object has been changed. If the change occurs before the object has been mutated it's possible that another
> + * thread may re-store the object before the changes are actually made!
> * <p>
> - * Note that (due to TAP5-834), the object will receive a spurious
> - * <code>valueUnbound()</code> notification when dirty. Tapestry sets dirty
> - * session attributes to null, then to the persisted object, to force a
> - * <code>valueBound()</code> notification, and that unfortunately also sends the
> - * <code>valueUnbound()</code>.
> - *
> * @since 5.1.1.0
> */
> -public abstract class BaseOptimizedSessionPersistedObject implements
> - OptimizedSessionPersistedObject, HttpSessionBindingListener
> +public abstract class BaseOptimizedSessionPersistedObject implements OptimizedSessionPersistedObject
> {
> - private transient boolean dirty;
> -
> - public final boolean isSessionPersistedObjectDirty()
> - {
> - return dirty;
> - }
> + private transient AtomicBoolean dirty = new AtomicBoolean(false);
>
> - /**
> - * Invoked by the servlet container when the value is stored (or re-stored)
> - * as an attribute of the session. This
> - * clears the dirty flag. Subclasses may override this method, but should
> - * invoke this implementation.
> - */
> - public void valueBound(HttpSessionBindingEvent event)
> - {
> - dirty = false;
> - }
> -
> - /**
> - * Does nothing.
> - */
> - public void valueUnbound(HttpSessionBindingEvent event)
> + public final boolean checkAndResetDirtyMarker()
> {
> + return dirty.getAndSet(false);
> }
>
> /**
> - * Invoked by the subclass whenever the internal state of the object
> - * changes. Typically, this is invoked from
> - * mutator methods.
> + * Invoked by the subclass after internal state of the object changes.
> */
> protected final void markDirty()
> {
> - dirty = true;
> + dirty.set(true);
> }
> }
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/OptimizedSessionPersistedObject.java Sat Jun 25 12:31:36 2011
> @@ -1,4 +1,4 @@
> -// Copyright 2008 The Apache Software Foundation
> +// Copyright 2008, 2011 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.
> @@ -17,9 +17,11 @@ package org.apache.tapestry5;
> /**
> * An optional interface implemented by objects that are persisted in the {@link org.apache.tapestry5.services.Session}.
> * At the end of each request, any objects read from the session are re-stored into the session, to ensure that
> - * in-memory changes are flushed to other servers in a cluster. Objects that implement this interface are expected to
> - * track when they are dirty (have pending changes), so that the save back into the session can be avoided when not
> - * necessary.
> + * in-memory changes are flushed to other persistent session stores (e.g. RDBMS, servers in a cluster, etc). Objects
> + * that implement this interface are expected to track when they are dirty (have pending changes), so that the save
> + * back into the session can be avoided when not necessary.
> + * <p>
> + * This method is accessed concurrently.
> *
> * @see org.apache.tapestry5.annotations.ImmutableSessionPersistedObject
> * @see org.apache.tapestry5.services.SessionPersistedObjectAnalyzer
> @@ -28,10 +30,7 @@ package org.apache.tapestry5;
> public interface OptimizedSessionPersistedObject
> {
> /**
> - * Returns true if the object has in-memory changes. It is the object's responsibility to set its internal flag to
> - * false, typically by implementing {@link javax.servlet.http.HttpSessionBindingListener}.
> - *
> - * @return
> + * @return true if the object has in-memory changes since the last time this method was called.
> */
> - boolean isSessionPersistedObjectDirty();
> + boolean checkAndResetDirtyMarker();
> }
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/SymbolConstants.java Sat Jun 25 12:31:36 2011
> @@ -294,4 +294,16 @@ public class SymbolConstants
> * @since 5.3.0
> */
> public static final String MINIFICATION_ENABLED = "tapestry.enable-minification";
> +
> + /**
> + * If "true" then at the end of each request the
> + * {@link org.apache.tapestry5.services.SessionPersistedObjectAnalyzer} will be called on each session persisted
> + * object that was accessed during the request.
> + * <p>
> + * This is provided as a performance enhancement for servers that do not use clustered sessions.
> + *
> + * @since 5.3.1
> + */
> + public static final String CLUSTERED_SESSIONS = "tapestry.clustered-sessions";
> +
> }
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/ValidationTrackerImpl.java Sat Jun 25 12:31:36 2011
> @@ -1,4 +1,4 @@
> -// Copyright 2006, 2008, 2010 The Apache Software Foundation
> +// Copyright 2006, 2008, 2010, 2011 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.
> @@ -84,8 +84,6 @@ public final class ValidationTrackerImpl
>
> private void store(FieldTracker fieldTracker)
> {
> - markDirty();
> -
> if (fieldTrackers == null)
> fieldTrackers = CollectionFactory.newList();
>
> @@ -98,15 +96,17 @@ public final class ValidationTrackerImpl
> fieldTrackers.add(fieldTracker);
> fieldToTracker.put(key, fieldTracker);
> }
> +
> + markDirty();
> }
>
> public void clear()
> {
> - markDirty();
> -
> extraErrors = null;
> fieldTrackers = null;
> fieldToTracker = null;
> +
> + markDirty();
> }
>
> public String getError(Field field)
> @@ -161,12 +161,12 @@ public final class ValidationTrackerImpl
>
> public void recordError(String errorMessage)
> {
> - markDirty();
> -
> if (extraErrors == null)
> extraErrors = CollectionFactory.newList();
>
> extraErrors.add(errorMessage);
> +
> + markDirty();
> }
>
> public void recordInput(Field field, String input)
>
> Copied: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java (from r1139143, tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java)
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java?p2=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java&p1=tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java&r1=1139143&r2=1139536&rev=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ClusteredSessionImpl.java Sat Jun 25 12:31:36 2011
> @@ -1,41 +1,33 @@
> -// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
> +// Copyright 2011 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.
> -// You may obtain a copy of the License at
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> //
> -// http://www.apache.org/licenses/LICENSE-2.0
> +// http://www.apache.org/licenses/LICENSE-2.0
> //
> -// Unless required by applicable law or agreed to in writing, software
> -// distributed under the License is distributed on an "AS IS" BASIS,
> -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> -// See the License for the specific language governing permissions and
> -// limitations under the License.
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
>
> package org.apache.tapestry5.internal.services;
>
> import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
> -import org.apache.tapestry5.ioc.internal.util.InternalUtils;
> -import org.apache.tapestry5.services.Session;
> import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
>
> +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.Map;
>
> /**
> - * A thin wrapper around {@link HttpSession}.
> + * A thin wrapper around {@link javax.servlet.http.HttpSession}.
> */
> -public class SessionImpl implements Session
> +public class ClusteredSessionImpl extends SessionImpl
> {
> private final SessionPersistedObjectAnalyzer analyzer;
>
> - private final HttpSession session;
> -
> - private boolean invalidated = false;
> -
> /**
> * Cache of attribute objects read from, or written to, the real session.
> * This is needed for end-of-request
> @@ -43,77 +35,42 @@ public class SessionImpl implements Sess
> */
> private final Map<String, Object> sessionAttributeCache = CollectionFactory.newMap();
>
> - public SessionImpl(HttpSession session, SessionPersistedObjectAnalyzer analyzer)
> + public ClusteredSessionImpl(
> + HttpServletRequest request,
> + HttpSession session,
> + SessionPersistedObjectAnalyzer analyzer)
> {
> - this.session = session;
> + super(request, session);
> this.analyzer = analyzer;
> }
>
> + @Override
> public Object getAttribute(String name)
> {
> - Object result = session.getAttribute(name);
> + Object result = super.getAttribute(name);
>
> sessionAttributeCache.put(name, result);
>
> return result;
> }
>
> - public List<String> getAttributeNames()
> - {
> - return InternalUtils.toList(session.getAttributeNames());
> - }
> -
> public void setAttribute(String name, Object value)
> {
> - session.setAttribute(name, value);
> + super.setAttribute(name, value);
>
> sessionAttributeCache.put(name, value);
> }
>
> - public List<String> getAttributeNames(String prefix)
> - {
> - List<String> result = CollectionFactory.newList();
> -
> - Enumeration e = session.getAttributeNames();
> - while (e.hasMoreElements())
> - {
> - String name = (String) e.nextElement();
> -
> - if (name.startsWith(prefix)) result.add(name);
> - }
> -
> - Collections.sort(result);
> -
> - return result;
> - }
> -
> - public int getMaxInactiveInterval()
> - {
> - return session.getMaxInactiveInterval();
> - }
> -
> public void invalidate()
> {
> - invalidated = true;
> -
> - session.invalidate();
> + super.invalidate();
>
> sessionAttributeCache.clear();
> }
>
> - public boolean isInvalidated()
> - {
> - return invalidated;
> - }
> -
> - public void setMaxInactiveInterval(int seconds)
> - {
> - session.setMaxInactiveInterval(seconds);
> - }
> -
> public void restoreDirtyObjects()
> {
> - if (invalidated) return;
> + if (isInvalidated()) return;
>
> if (sessionAttributeCache.isEmpty()) return;
>
> @@ -125,15 +82,9 @@ public class SessionImpl implements Sess
>
> if (attributeValue == null) continue;
>
> - if (analyzer.isDirty(attributeValue))
> + if (analyzer.checkAndResetDirtyState(attributeValue))
> {
> - // TAP5-834: Jetty & Tomcat work by object identity, will not update the attribute
> - // and fire the session binding event unless there's a real change. So we set the
> - // attribute to null and then to the new value and that should force the necessary
> - // notification.
> -
> - session.setAttribute(attributeName, null);
> - session.setAttribute(attributeName, attributeValue);
> + super.setAttribute(attributeName, attributeValue);
> }
> }
> }
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultSessionPersistedObjectAnalyzer.java Sat Jun 25 12:31:36 2011
> @@ -1,4 +1,4 @@
> -// Copyright 2008 The Apache Software Foundation
> +// Copyright 2008, 2011 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.
> @@ -28,14 +28,14 @@ public class DefaultSessionPersistedObje
> * An object is dirty <em>unless</em> it has the {@link org.apache.tapestry5.annotations.ImmutableSessionPersistedObject}
> * annotation.
> *
> - * @param object to analyze
> + * @param sessionPersistedObject to analyze
> * @return false if immutable, true otherwise
> */
> - public boolean isDirty(Object object)
> + public boolean checkAndResetDirtyState(Object sessionPersistedObject)
> {
> - boolean immutable = object.getClass().getAnnotation(ImmutableSessionPersistedObject.class) != null;
> + boolean immutable = sessionPersistedObject.getClass().getAnnotation(ImmutableSessionPersistedObject.class) != null;
>
> - // Imuutable objects are always clean, others are assumed dirty.
> + // Immutable objects are always clean, others are assumed dirty.
> // Go implement OptimizedSessionPersistedObject if you don't like it.
>
> return !immutable;
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/OptimizedSessionPersistedObjectAnalyzer.java Sat Jun 25 12:31:36 2011
> @@ -1,4 +1,4 @@
> -// Copyright 2008 The Apache Software Foundation
> +// Copyright 2008, 2011 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.
> @@ -19,8 +19,8 @@ import org.apache.tapestry5.services.Ses
>
> public class OptimizedSessionPersistedObjectAnalyzer implements SessionPersistedObjectAnalyzer<OptimizedSessionPersistedObject>
> {
> - public boolean isDirty(OptimizedSessionPersistedObject object)
> + public boolean checkAndResetDirtyState(OptimizedSessionPersistedObject sessionPersistedObject)
> {
> - return object.isSessionPersistedObjectDirty();
> + return sessionPersistedObject.checkAndResetDirtyMarker();
> }
> }
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestImpl.java Sat Jun 25 12:31:36 2011
> @@ -17,10 +17,8 @@ package org.apache.tapestry5.internal.se
> import org.apache.tapestry5.ioc.internal.util.InternalUtils;
> import org.apache.tapestry5.services.Request;
> import org.apache.tapestry5.services.Session;
> -import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
>
> import javax.servlet.http.HttpServletRequest;
> -import javax.servlet.http.HttpSession;
> import java.io.UnsupportedEncodingException;
> import java.util.List;
> import java.util.Locale;
> @@ -39,19 +37,20 @@ public class RequestImpl implements Requ
>
> private final String requestEncoding;
>
> - private final SessionPersistedObjectAnalyzer analyzer;
> + private final SessionFactory sessionFactory;
>
> private boolean encodingSet;
>
> - HttpSession hsession;
> -
> Session session;
>
> - public RequestImpl(HttpServletRequest request, String requestEncoding, SessionPersistedObjectAnalyzer analyzer)
> + public RequestImpl(
> + HttpServletRequest request,
> + String requestEncoding,
> + SessionFactory sessionFactory)
> {
> this.request = request;
> this.requestEncoding = requestEncoding;
> - this.analyzer = analyzer;
> + this.sessionFactory = sessionFactory;
> }
>
> public List<String> getParameterNames()
> @@ -105,27 +104,15 @@ public class RequestImpl implements Requ
>
> public Session getSession(boolean create)
> {
> - if (session != null)
> + if (session != null && session.isInvalidated())
> {
> - // The easy case is when the session was invalidated through the Tapestry Session
> - // object. The hard case is when the HttpSession was invalidated outside of Tapestry,
> - // in which case, request.getSession() will return a new HttpSession instance (or null)
> -
> - if (session.isInvalidated() || hsession != request.getSession(false))
> - {
> - session = null;
> - hsession = null;
> - }
> + session = null;
> }
>
> - if (session == null)
> + if (session == null )
> {
> - hsession = request.getSession(create);
> -
> - if (hsession != null)
> - {
> - session = new SessionImpl(hsession, analyzer);
> - }
> + // TAP5-1489 - Re-storage of session attributes at end of request should be configurable
> + session = sessionFactory.getSession(create);
> }
>
> return session;
> @@ -149,8 +136,7 @@ public class RequestImpl implements Requ
> try
> {
> request.setCharacterEncoding(requestEncoding);
> - }
> - catch (UnsupportedEncodingException ex)
> + } catch (UnsupportedEncodingException ex)
> {
> throw new RuntimeException(ex);
> }
> @@ -198,7 +184,9 @@ public class RequestImpl implements Requ
> return request.getLocalPort();
> }
>
> - /** @since 5.2.5 */
> + /**
> + * @since 5.2.5
> + */
> public int getServerPort()
> {
> return request.getServerPort();
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactory.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,22 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.internal.services;
> +
> +import org.apache.tapestry5.services.Session;
> +
> +public interface SessionFactory
> +{
> + Session getSession(boolean create);
> +}
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionFactoryImpl.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,59 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.internal.services;
> +
> +import org.apache.tapestry5.SymbolConstants;
> +import org.apache.tapestry5.ioc.annotations.Symbol;
> +import org.apache.tapestry5.services.Session;
> +import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
> +
> +import javax.servlet.http.HttpServletRequest;
> +import javax.servlet.http.HttpSession;
> +
> +/**
> + * User: josh_canfield
> + * Date: 6/25/11
> + */
> +public class SessionFactoryImpl implements SessionFactory
> +{
> + private boolean clustered;
> + private final SessionPersistedObjectAnalyzer analyzer;
> + private final HttpServletRequest request;
> +
> + public SessionFactoryImpl(
> + @Symbol(SymbolConstants.CLUSTERED_SESSIONS)
> + boolean clustered,
> + SessionPersistedObjectAnalyzer analyzer,
> + HttpServletRequest request)
> + {
> + this.clustered = clustered;
> + this.analyzer = analyzer;
> + this.request = request;
> + }
> +
> + public Session getSession(boolean create)
> + {
> + final HttpSession httpSession = request.getSession(create);
> +
> + if (httpSession == null ) return null;
> +
> + if (clustered)
> + {
> + return new ClusteredSessionImpl(request, httpSession, analyzer);
> + }
> +
> + return new SessionImpl(request, httpSession);
> + }
> +}
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SessionImpl.java Sat Jun 25 12:31:36 2011
> @@ -1,4 +1,4 @@
> -// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
> +// Copyright 2006, 2007, 2008, 2009, 2011 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.
> @@ -19,6 +19,7 @@ import org.apache.tapestry5.ioc.internal
> import org.apache.tapestry5.services.Session;
> import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
>
> +import javax.servlet.http.HttpServletRequest;
> import javax.servlet.http.HttpSession;
> import java.util.Collections;
> import java.util.Enumeration;
> @@ -30,32 +31,20 @@ import java.util.Map;
> */
> public class SessionImpl implements Session
> {
> - private final SessionPersistedObjectAnalyzer analyzer;
> -
> + private final HttpServletRequest request;
> private final HttpSession session;
>
> private boolean invalidated = false;
>
> - /**
> - * Cache of attribute objects read from, or written to, the real session.
> - * This is needed for end-of-request
> - * processing.
> - */
> - private final Map<String, Object> sessionAttributeCache = CollectionFactory.newMap();
> -
> - public SessionImpl(HttpSession session, SessionPersistedObjectAnalyzer analyzer)
> + public SessionImpl(HttpServletRequest request, HttpSession session)
> {
> + this.request = request;
> this.session = session;
> - this.analyzer = analyzer;
> }
>
> public Object getAttribute(String name)
> {
> - Object result = session.getAttribute(name);
> -
> - sessionAttributeCache.put(name, result);
> -
> - return result;
> + return session.getAttribute(name);
> }
>
> public List<String> getAttributeNames()
> @@ -66,8 +55,6 @@ public class SessionImpl implements Sess
> public void setAttribute(String name, Object value)
> {
> session.setAttribute(name, value);
> -
> - sessionAttributeCache.put(name, value);
> }
>
> public List<String> getAttributeNames(String prefix)
> @@ -97,12 +84,18 @@ public class SessionImpl implements Sess
> invalidated = true;
>
> session.invalidate();
> -
> - sessionAttributeCache.clear();
> }
>
> public boolean isInvalidated()
> {
> + if (invalidated) return true;
> +
> + // The easy case is when the session was invalidated through the Tapestry Session
> + // object. The hard case is when the HttpSession was invalidated outside of Tapestry,
> + // in which case, request.getSession() will return a new HttpSession instance (or null)
> +
> + invalidated = request.getSession(false) != session;
> +
> return invalidated;
> }
>
> @@ -113,28 +106,6 @@ public class SessionImpl implements Sess
>
> public void restoreDirtyObjects()
> {
> - if (invalidated) return;
> -
> - if (sessionAttributeCache.isEmpty()) return;
> -
> - for (Map.Entry<String, Object> entry : sessionAttributeCache.entrySet())
> - {
> - String attributeName = entry.getKey();
> -
> - Object attributeValue = entry.getValue();
>
> - if (attributeValue == null) continue;
> -
> - if (analyzer.isDirty(attributeValue))
> - {
> - // TAP5-834: Jetty & Tomcat work by object identity, will not update the attribute
> - // and fire the session binding event unless there's a real change. So we set the
> - // attribute to null and then to the new value and that should force the necessary
> - // notification.
> -
> - session.setAttribute(attributeName, null);
> - session.setAttribute(attributeName, attributeValue);
> - }
> - }
> }
> }
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/Session.java Sat Jun 25 12:31:36 2011
> @@ -72,8 +72,8 @@ public interface Session
> void invalidate();
>
> /**
> - * Checks to see if the session has been invalidated. Note: this only catches calls to {@link #invalidate()}, not
> - * calls to {@link javax.servlet.http.HttpSession#invalidate()}.
> + * Checks to see if the session has been invalidated. Note: since 5.3.1 this will also catch calls to
> + * {@link javax.servlet.http.HttpSession#invalidate()}.
> *
> * @since 5.1.0.0
> */
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/SessionPersistedObjectAnalyzer.java Sat Jun 25 12:31:36 2011
> @@ -1,4 +1,4 @@
> -// Copyright 2008 The Apache Software Foundation
> +// Copyright 2008, 2011 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.
> @@ -17,10 +17,15 @@ package org.apache.tapestry5.services;
> import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
>
> /**
> - * Analyzes a session-persisted object, specifically to see if it is dirty or not. The service implementation uses a
> - * mapped configuration to form a {@linkplain org.apache.tapestry5.ioc.services.StrategyBuilder strategy} based on
> - * object type. The service is injectable using the {@link org.apache.tapestry5.ioc.annotations.Primary} marker
> - * annotation.
> + * Analyzes a session-persisted object, specifically to see if it is dirty or not.
> + * <p/>
> + * This service is provided to support applications which store mutable session attributes where the
> + * session is replicated to a slower medium (e.g. RDMBS, Cluster, etc) this can help alleviate excessive writes
> + * to the session store while ensuring changes are propagated.
> + * <p/>
> + * The service implementation uses a mapped configuration to form a
> + * {@linkplain org.apache.tapestry5.ioc.services.StrategyBuilder strategy} based on object type. The service may be
> + * injected using the {@link org.apache.tapestry5.ioc.annotations.Primary} marker annotation.
> *
> * @see org.apache.tapestry5.annotations.ImmutableSessionPersistedObject
> * @see org.apache.tapestry5.OptimizedSessionPersistedObject
> @@ -30,11 +35,15 @@ import org.apache.tapestry5.ioc.annotati
> public interface SessionPersistedObjectAnalyzer<T>
> {
> /**
> - * Passed an object (never null) to see if it is dirty or not. Dirty objects that are stored in the session are
> - * re-stored into the session at the end of the request.
> + * Atomically check and reset the dirty state of the session persisted object.
> + * <p/>
> + * The implementer should take consideration for the fact that session attributes are accessed concurrently. A
> + * naive check/set algorithm may allow changes to go un-noticed.
> *
> - * @param object
> - * @return true if object needs to be re-stored into the session
> + * @param sessionPersistedObject the session attribute (never null)
> + * @return true if the object needs to be re-stored into the session
> + * @since 5.3.1
> */
> - boolean isDirty(T object);
> + boolean checkAndResetDirtyState(T sessionPersistedObject);
> +
> }
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Sat Jun 25 12:31:36 2011
> @@ -355,14 +355,14 @@ public final class TapestryModule
> {
> private final RequestHandler handler;
> private final String applicationCharset;
> - private final SessionPersistedObjectAnalyzer analyzer;
> + private final SessionFactory sessionFactory;
>
> public HttpServletRequestHandlerTerminator(RequestHandler handler, String applicationCharset,
> - SessionPersistedObjectAnalyzer analyzer)
> + SessionFactory sessionFactory)
> {
> this.handler = handler;
> this.applicationCharset = applicationCharset;
> - this.analyzer = analyzer;
> + this.sessionFactory = sessionFactory;
> }
>
> public boolean service(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
> @@ -370,7 +370,7 @@ public final class TapestryModule
> {
> requestGlobals.storeServletRequestResponse(servletRequest, servletResponse);
>
> - Request request = new RequestImpl(servletRequest, applicationCharset, analyzer);
> + Request request = new RequestImpl(servletRequest, applicationCharset, sessionFactory);
> Response response = new ResponseImpl(servletRequest, servletResponse);
>
> // TAP5-257: Make sure that the "initial guess" for request/response
> @@ -484,6 +484,7 @@ public final class TapestryModule
> binder.bind(ContextPathEncoder.class, ContextPathEncoderImpl.class);
> binder.bind(ApplicationStatePersistenceStrategy.class, SessionApplicationStatePersistenceStrategy.class)
> .withId("SessionApplicationStatePersistenceStrategy");
> + binder.bind(SessionFactory.class, SessionFactoryImpl.class);
> binder.bind(AssetPathConverter.class, IdentityAssetPathConverter.class);
> binder.bind(NumericTranslatorSupport.class);
> binder.bind(ClientDataEncoder.class, ClientDataEncoderImpl.class);
> @@ -1115,7 +1116,7 @@ public final class TapestryModule
> * Adds coercions:
> * <ul>
> * <li>String to {@link SelectModel}
> - * <li>Map to {@link oSelectModel}
> + * <li>Map to {@link SelectModel}
> * <li>Collection to {@link GridDataSource}
> * <li>null to {@link GridDataSource}
> * <li>List to {@link SelectModel}
> @@ -1516,11 +1517,10 @@ public final class TapestryModule
> @Symbol(SymbolConstants.CHARSET)
> String applicationCharset,
>
> - @Primary
> - SessionPersistedObjectAnalyzer analyzer)
> + SessionFactory sessionFactory)
> {
> HttpServletRequestHandler terminator = new HttpServletRequestHandlerTerminator(handler, applicationCharset,
> - analyzer);
> + sessionFactory);
>
> return pipelineBuilder.build(logger, HttpServletRequestHandler.class, HttpServletRequestFilter.class,
> configuration, terminator);
> @@ -2457,6 +2457,8 @@ public final class TapestryModule
>
> configuration.add(SymbolConstants.PRODUCTION_MODE, true);
>
> + configuration.add(SymbolConstants.CLUSTERED_SESSIONS, true);
> +
> configuration.add(SymbolConstants.COMPRESS_WHITESPACE, true);
>
> configuration.add(MetaDataConstants.SECURE_PAGE, false);
> @@ -2704,7 +2706,7 @@ public final class TapestryModule
> }
>
> /**
> - * The master SessionPesistedObjectAnalyzer.
> + * The master SessionPersistedObjectAnalyzer.
> *
> * @since 5.1.0.0
> */
> @@ -2730,7 +2732,7 @@ public final class TapestryModule
>
> SessionPersistedObjectAnalyzer<Object> immutable = new SessionPersistedObjectAnalyzer<Object>()
> {
> - public boolean isDirty(Object object)
> + public boolean checkAndResetDirtyState(Object sessionPersistedObject)
> {
> return false;
> }
>
> Copied: tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml (from r1139143, tapestry/tapestry5/trunk/tapestry-core/src/test/app5/WEB-INF/web.xml)
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml?p2=tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml&p1=tapestry/tapestry5/trunk/tapestry-core/src/test/app5/WEB-INF/web.xml&r1=1139143&r2=1139536&rev=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/app5/WEB-INF/web.xml (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/cluster/WEB-INF/web.xml Sat Jun 25 12:31:36 2011
> @@ -3,10 +3,10 @@
> PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
> "http://java.sun.com/dtd/web-app_2_3.dtd">
> <web-app>
> - <display-name>Integration Test App 5 -- Skinning</display-name>
> + <display-name>Integration Test Cluster</display-name>
> <context-param>
> <param-name>tapestry.app-package</param-name>
> - <param-value>org.apache.tapestry5.integration.app5</param-value>
> + <param-value>org.apache.tapestry5.integration.cluster</param-value>
> </context-param>
> <filter>
> <filter-name>app</filter-name>
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/ClusterTests.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,248 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster;
> +
> +import com.thoughtworks.selenium.DefaultSelenium;
> +import com.thoughtworks.selenium.Selenium;
> +import org.apache.tapestry5.test.Jetty7Runner;
> +import org.apache.tapestry5.test.TapestryTestConstants;
> +import org.eclipse.jetty.server.Server;
> +import org.eclipse.jetty.server.session.JDBCSessionIdManager;
> +import org.eclipse.jetty.server.session.JDBCSessionManager;
> +import org.eclipse.jetty.server.session.SessionHandler;
> +import org.eclipse.jetty.webapp.WebAppContext;
> +import org.openqa.selenium.server.RemoteControlConfiguration;
> +import org.openqa.selenium.server.SeleniumServer;
> +import org.testng.annotations.AfterClass;
> +import org.testng.annotations.BeforeClass;
> +import org.testng.annotations.Test;
> +import org.testng.xml.XmlTest;
> +
> +import java.sql.Connection;
> +import java.sql.DriverManager;
> +import java.sql.SQLException;
> +import java.sql.Statement;
> +
> +import static org.testng.AssertJUnit.assertEquals;
> +import static org.testng.AssertJUnit.assertTrue;
> +
> +/**
> + * User: josh_canfield Date: 6/24/11
> + */
> +public class ClusterTests
> +{
> + private static final String FIREFOX_BROWSER_CMD = "*firefox";
> + private static final int SERVER_A_PORT = 9091;
> + private static final int SERVER_B_PORT = 9092;
> + private static final String SERVER_A_NAME = "server_A";
> + private static final String SERVER_B_NAME = "server_B";
> +
> + private static final String CREATE_1 = "//a[contains(text(),'create1')]";
> + private static final String VALUE_1 = "value1";
> +
> + private static final String CREATE_2 = "//a[contains(text(),'create2')]";
> + private static final String VALUE_2 = "value2";
> +
> + private static final String UPDATE_1 = "//a[contains(text(),'update1')]";
> + private static final String VALUE_3 = "value3";
> +
> + private static final String UPDATE_2 = "//a[contains(text(),'update2')]";
> + private static final String VALUE_4 = "value4";
> +
> + private static final String CLEAR = "//a[contains(text(),'Clear')]";
> +
> + Jetty7Runner serverA;
> +
> + Jetty7Runner serverB;
> +
> + SeleniumServer seleniumServer;
> + Selenium selenium;
> +
> + @BeforeClass
> + void setupServers(XmlTest xmlTest) throws Exception
> + {
> + createJettySessionsTable();
> +
> + serverA = configureClusteredJetty(SERVER_A_NAME, SERVER_A_PORT);
> + serverB = configureClusteredJetty(SERVER_B_NAME, SERVER_B_PORT);
> +
> + seleniumServer = new SeleniumServer();
> + seleniumServer.start();
> +
> + String browserStartCommand = xmlTest.getParameter(TapestryTestConstants.BROWSER_START_COMMAND_PARAMETER);
> + browserStartCommand = browserStartCommand != null ? browserStartCommand : FIREFOX_BROWSER_CMD;
> +
> + selenium = new DefaultSelenium(
> + "localhost", RemoteControlConfiguration.DEFAULT_PORT,
> + browserStartCommand, "http://localhost:9091/"
> + );
> + selenium.start();
> + }
> +
> + @AfterClass
> + void stopServers()
> + {
> + serverA.stop();
> + serverB.stop();
> + selenium.stop();
> + seleniumServer.stop();
> + }
> +
> + @Test
> + public void mutable_pojo_as_session_state_is_always_shared()
> + {
> + // Expect all object changes to be transferred to the other server.
> + String[][] click = {
> + {CREATE_1, VALUE_1, VALUE_1},
> + {CLEAR, "", ""},
> + {CREATE_2, VALUE_2, VALUE_2},
> + {UPDATE_1, VALUE_3, VALUE_3},
> + {UPDATE_2, VALUE_4, VALUE_4}
> + };
> +
> + evaluate("PersistedMutablePojoDemo", click);
> + }
> +
> + @Test
> + public void immutable_session_persisted_object() throws InterruptedException
> + {
> + // expect only create links to transfer over (We've told tapestry it's immutable)
> + String[][] data = {
> + {CREATE_1, VALUE_1, VALUE_1},
> + {UPDATE_1, VALUE_3, VALUE_1},
> + {CLEAR, "", ""},
> + {CREATE_2, VALUE_2, VALUE_2},
> + {UPDATE_2, VALUE_4, VALUE_2},
> + {UPDATE_1, VALUE_3, VALUE_2},
> + };
> +
> + evaluate("ImmutableSessionPersistedObjectDemo", data);
> + }
> +
> + @Test
> + public void session_persisted_object_analyzer() throws InterruptedException
> + {
> + // special cased so that only UPDATE_2 marks as dirty, creates/deletes still transfer
> + String[][] data = {
> + {CREATE_1, VALUE_1, VALUE_1}, // created, session transferred
> + {UPDATE_1, VALUE_3, VALUE_1}, // update-1 doesn't transfer
> + {CLEAR, "", ""},
> + {CREATE_2, VALUE_2, VALUE_2}, // create-2, session transferred
> + {UPDATE_2, VALUE_4, VALUE_4}, // update-2, session transferred
> + {UPDATE_1, VALUE_3, VALUE_4}, // update-1, doesn't transfer
> + };
> +
> + evaluate("SessionPersistedObjectAnalyzerDemo", data);
> + }
> +
> + private void evaluate(String page, String[][] expect)
> + {
> + for (String[] strings : expect)
> + {
> + openOnServerA(page);
> +
> + clickAndWait(strings[0]);
> +
> + assertText("value", strings[1]);
> +
> + openOnServerB(page);
> +
> + assertText("value", strings[2]);
> + }
> +
> + clickAndWait(CLEAR);
> + assertText("value", "");
> + }
> +
> + private void openOnServerA(String page)
> + {
> + selenium.open("http://localhost:" + SERVER_A_PORT + "/" + page);
> + assertServerName(SERVER_A_NAME);
> + }
> +
> + private void openOnServerB(String page)
> + {
> + selenium.open("http://localhost:" + SERVER_B_PORT + "/" + page);
> + assertServerName(SERVER_B_NAME);
> + }
> +
> + private void assertServerName(String serverName)
> + {
> + assertTrue(selenium.isElementPresent("//h1[@id='serverName' and text()='" + serverName + "']"));
> + }
> +
> + private void assertText(String locator, String expected)
> + {
> + assertEquals(expected, selenium.getText(locator));
> + }
> +
> + private void clickAndWait(String s)
> + {
> + selenium.click(s);
> + selenium.waitForPageToLoad("5000");
> + }
> +
> + private Jetty7Runner configureClusteredJetty(String name, int port) throws Exception
> + {
> + Jetty7Runner runner = new Jetty7Runner();
> +
> + runner.configure("src/test/cluster", "", port, port + 100);
> +
> + JDBCSessionIdManager idMgr = new JDBCSessionIdManager(runner.getServer());
> + idMgr.setWorkerName(name);
> + idMgr.setDriverInfo("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:clustertest");
> +
> + Server server = runner.getServer();
> + server.setSessionIdManager(idMgr);
> +
> + WebAppContext wac = (WebAppContext) server.getHandler();
> +
> + JDBCSessionManager jdbcMgr = new JDBCSessionManager();
> + jdbcMgr.setIdManager(server.getSessionIdManager());
> +
> + // force the session to be read from the database with no delay
> + // This is an incorrectly documented feature.
> + jdbcMgr.setSaveInterval(0);
> +
> + wac.setSessionHandler(new SessionHandler(jdbcMgr));
> + wac.getServletContext().setInitParameter("cluster.name", name);
> + runner.start();
> + return runner;
> + }
> +
> + private void createJettySessionsTable() throws ClassNotFoundException, SQLException
> + {
> + Class.forName("org.hsqldb.jdbcDriver");
> +
> + Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:clustertest", "sa", "");
> + String sql = "create table JettySessions (" +
> + "rowId varchar(60), " +
> + "sessionId varchar(60)," +
> + "contextPath varchar(60)," +
> + "virtualHost varchar(60)," +
> + "lastNode varchar(60)," +
> + "accessTime bigint," +
> + "lastAccessTime bigint," +
> + "createTime bigint," +
> + "cookieTime bigint," +
> + "lastSavedTime bigint," +
> + "expiryTime bigint," +
> + "map longvarbinary" +
> + ");";
> + Statement statement = c.createStatement();
> + statement.execute(sql);
> + statement.close();
> + }
> +}
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,41 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster.base;
> +
> +import org.apache.tapestry5.annotations.Persist;
> +import org.apache.tapestry5.annotations.Property;
> +import org.apache.tapestry5.integration.cluster.data.SessionStateObject;
> +
> +public abstract class BaseSessionDemo<T extends SessionStateObject> {
> +
> + @Property
> + @Persist
> + private SessionStateObject data;
> +
> + abstract public T create(String value);
> +
> + void onCreateValue(String value) {
> + data = create(value);
> + }
> +
> + void onChangeValue(String value) {
> + data.setValue(value);
> + }
> +
> + void onClear() {
> + data = null;
> + }
> +
> +}
> \ No newline at end of file
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/AnalyzedSessionObject.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,45 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster.data;
> +
> +public class AnalyzedSessionObject implements SessionStateObject
> +{
> + private String value;
> +
> + boolean dirty = false;
> +
> + public AnalyzedSessionObject(String value)
> + {
> + this.value = value;
> + }
> +
> + public boolean checkAndResetDirtyState()
> + {
> + boolean check = dirty;
> + dirty = false;
> + return check;
> + }
> +
> + public String getValue()
> + {
> + return value;
> + }
> +
> + public void setValue(String value)
> + {
> + this.value = value;
> + dirty = "value4".equals(value);
> + }
> +}
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/ImmutableByAnnotation.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,34 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster.data;
> +
> +import org.apache.tapestry5.annotations.ImmutableSessionPersistedObject;
> +
> +@ImmutableSessionPersistedObject
> +public class ImmutableByAnnotation implements SessionStateObject {
> + private String value;
> +
> + public ImmutableByAnnotation(String value) {
> + this.value = value;
> + }
> +
> + public String getValue() {
> + return value;
> + }
> +
> + public void setValue(String value) {
> + this.value = value;
> + }
> +}
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/MutablePojo.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,32 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster.data;
> +
> +public class MutablePojo implements SessionStateObject {
> +
> + private String value;
> +
> + public MutablePojo(String value) {
> + this.value = value;
> + }
> +
> + public String getValue() {
> + return value;
> + }
> +
> + public void setValue(String name) {
> + this.value = name;
> + }
> +}
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/data/SessionStateObject.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,24 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster.data;
> +
> +import java.io.Serializable;
> +
> +public interface SessionStateObject extends Serializable {
> +
> + String getValue();
> +
> + void setValue(String v);
> +}
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/ImmutableSessionPersistedObjectDemo.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,29 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster.pages;
> +
> +import org.apache.tapestry5.integration.cluster.base.BaseSessionDemo;
> +import org.apache.tapestry5.integration.cluster.data.ImmutableByAnnotation;
> +
> +/**
> + * User: josh_canfield
> + * Date: 6/24/11
> + */
> +public class ImmutableSessionPersistedObjectDemo extends BaseSessionDemo<ImmutableByAnnotation> {
> + @Override
> + public ImmutableByAnnotation create(String value) {
> + return new ImmutableByAnnotation(value);
> + }
> +}
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/PersistedMutablePojoDemo.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,26 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster.pages;
> +
> +import org.apache.tapestry5.integration.cluster.base.BaseSessionDemo;
> +import org.apache.tapestry5.integration.cluster.data.MutablePojo;
> +
> +public class PersistedMutablePojoDemo extends BaseSessionDemo<MutablePojo> {
> +
> + @Override
> + public MutablePojo create(String value) {
> + return new MutablePojo(value);
> + }
> +}
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/pages/SessionPersistedObjectAnalyzerDemo.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,25 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster.pages;
> +
> +import org.apache.tapestry5.integration.cluster.base.BaseSessionDemo;
> +import org.apache.tapestry5.integration.cluster.data.AnalyzedSessionObject;
> +
> +public class SessionPersistedObjectAnalyzerDemo extends BaseSessionDemo<AnalyzedSessionObject> {
> + @Override
> + public AnalyzedSessionObject create(String value) {
> + return new AnalyzedSessionObject(value);
> + }
> +}
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/cluster/services/AppModule.java Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,36 @@
> +// Copyright 2011 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.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.cluster.services;
> +
> +import org.apache.tapestry5.integration.cluster.data.AnalyzedSessionObject;
> +import org.apache.tapestry5.ioc.MappedConfiguration;
> +import org.apache.tapestry5.ioc.annotations.Contribute;
> +import org.apache.tapestry5.services.SessionPersistedObjectAnalyzer;
> +
> +public class AppModule
> +{
> +
> + @Contribute(SessionPersistedObjectAnalyzer.class)
> + public static void analyzer(MappedConfiguration<Class, SessionPersistedObjectAnalyzer> configuration)
> + {
> + configuration.add(AnalyzedSessionObject.class, new SessionPersistedObjectAnalyzer<AnalyzedSessionObject>()
> + {
> + public boolean checkAndResetDirtyState(AnalyzedSessionObject sessionPersistedObject)
> + {
> + return sessionPersistedObject.checkAndResetDirtyState();
> + }
> + });
> + }
> +}
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RequestImplTest.java Sat Jun 25 12:31:36 2011
> @@ -33,12 +33,13 @@ public class RequestImplTest extends Int
> public void get_session_doesnt_exist()
> {
> HttpServletRequest sr = mockHttpServletRequest();
> + SessionFactory sf = newMock(SessionFactory.class);
>
> - train_getSession(sr, false, null);
> + expect(sf.getSession(false)).andReturn(null);
>
> replay();
>
> - Request request = new RequestImpl(sr, CHARSET, null);
> + Request request = new RequestImpl(sr, CHARSET, sf);
>
> assertNull(request.getSession(false));
>
> @@ -50,14 +51,15 @@ public class RequestImplTest extends Int
> {
> HttpServletRequest sr = mockHttpServletRequest();
> HttpSession ss = mockHttpSession();
> + SessionFactory sf = newMock(SessionFactory.class);
>
> - train_getSession(sr, true, ss);
> + expect(sf.getSession(true)).andReturn(new SessionImpl(sr, ss));
>
> train_getAttribute(ss, "foo", "bar");
>
> replay();
>
> - Request request = new RequestImpl(sr, CHARSET, null);
> + Request request = new RequestImpl(sr, CHARSET, sf);
> Session session = request.getSession(true);
>
> assertEquals(session.getAttribute("foo"), "bar");
> @@ -202,11 +204,13 @@ public class RequestImplTest extends Int
> HttpSession hsession1 = mockHttpSession();
> HttpSession hsession2 = mockHttpSession();
>
> - train_getSession(sr, true, hsession1);
> + SessionFactory sf = newMock(SessionFactory.class);
> +
> + expect(sf.getSession(true)).andReturn(new SessionImpl(sr,hsession1));
>
> replay();
>
> - Request request = new RequestImpl(sr, CHARSET, null);
> + Request request = new RequestImpl(sr, CHARSET, sf);
>
> Session session1 = request.getSession(true);
>
> @@ -214,8 +218,11 @@ public class RequestImplTest extends Int
>
> hsession1.invalidate();
>
> - train_getSession(sr, false, hsession2);
> - train_getSession(sr, true, hsession2);
> + expect(sr.getSession(false)).andReturn(null);
> +
> + SessionImpl session = new SessionImpl(sr, hsession2);
> + expect(sf.getSession(true)).andReturn(session);
> + expect(sf.getSession(true)).andReturn(session);
>
> replay();
>
>
> Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java?rev=1139536&r1=1139535&r2=1139536&view=diff
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java (original)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/SessionImplTest.java Sat Jun 25 12:31:36 2011
> @@ -1,4 +1,4 @@
> -// Copyright 2006, 2007, 2008, 2009 The Apache Software Foundation
> +// Copyright 2006, 2007, 2008, 2009, 2011 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.
> @@ -18,6 +18,7 @@ import java.util.Arrays;
> import java.util.Collections;
> import java.util.Enumeration;
>
> +import javax.servlet.http.HttpServletRequest;
> import javax.servlet.http.HttpSession;
>
> import org.apache.tapestry5.internal.test.InternalBaseTestCase;
> @@ -37,7 +38,7 @@ public class SessionImplTest extends Int
>
> replay();
>
> - Session session = new SessionImpl(hs, null);
> + Session session = new SessionImpl(null, hs);
>
> assertEquals(session.getAttributeNames(), Arrays.asList("barney", "fred"));
>
> @@ -54,7 +55,7 @@ public class SessionImplTest extends Int
>
> replay();
>
> - Session session = new SessionImpl(hs, null);
> + Session session = new SessionImpl(null, hs);
>
> assertEquals(session.getAttributeNames("f"), Arrays.asList("fanny", "fred"));
>
> @@ -70,7 +71,7 @@ public class SessionImplTest extends Int
>
> replay();
>
> - Session session = new SessionImpl(hs, null);
> + Session session = new SessionImpl(null, hs);
>
> session.invalidate();
>
> @@ -78,6 +79,39 @@ public class SessionImplTest extends Int
> }
>
> @Test
> + public void http_session_invalidate()
> + {
> + HttpSession hs = mockHttpSession();
> +
> + HttpServletRequest hsr = mockHttpServletRequest();
> +
> + train_getSession(hsr, false, hs);
> +
> + replay();
> +
> + Session session = new SessionImpl(hsr, hs);
> +
> + assertFalse(session.isInvalidated());
> +
> + verify();
> +
> + train_getSession(hsr, false, null);
> +
> + replay();
> +
> + assertTrue(session.isInvalidated());
> +
> + verify();
> +
> + train_getSession(hsr, false, mockHttpSession());
> +
> + replay();
> +
> + assertTrue(session.isInvalidated());
> +
> + }
> +
> + @Test
> public void set_max_inactive()
> {
> HttpSession hs = mockHttpSession();
> @@ -87,7 +121,7 @@ public class SessionImplTest extends Int
>
> replay();
>
> - Session session = new SessionImpl(hs, null);
> + Session session = new SessionImpl(null, hs);
>
> session.setMaxInactiveInterval(seconds);
>
> @@ -104,7 +138,7 @@ public class SessionImplTest extends Int
>
> replay();
>
> - Session session = new SessionImpl(hs, null);
> + Session session = new SessionImpl(null, hs);
>
> assertEquals(session.getMaxInactiveInterval(), seconds);
>
> @@ -115,6 +149,7 @@ public class SessionImplTest extends Int
> public void dirty_persisted_object_is_forced_to_update()
> {
> HttpSession hs = mockHttpSession();
> + HttpServletRequest hsr = mockHttpServletRequest();
> SessionPersistedObjectAnalyzer analyzer = newMock(SessionPersistedObjectAnalyzer.class);
> Object dirty = new Object();
>
> @@ -122,15 +157,16 @@ public class SessionImplTest extends Int
>
> replay();
>
> - Session session = new SessionImpl(hs, analyzer);
> + Session session = new ClusteredSessionImpl(hsr, hs, analyzer);
>
> assertSame(session.getAttribute("dirty"), dirty);
>
> verify();
>
> - expect(analyzer.isDirty(dirty)).andReturn(true);
> + expect(analyzer.checkAndResetDirtyState(dirty)).andReturn(true);
> +
> + train_getSession(hsr, false, hs);
>
> - hs.setAttribute("dirty", null);
> hs.setAttribute("dirty", dirty);
>
> replay();
>
> Added: tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml
> URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml?rev=1139536&view=auto
> ==============================================================================
> --- tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml (added)
> +++ tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/cluster/base/BaseSessionDemo.tml Sat Jun 25 12:31:36 2011
> @@ -0,0 +1,21 @@
> +<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
> +<head>
> + <title>Immutable Annotation Demo</title>
> +</head>
> +
> +<body>
> +<h1 id="serverName">${symbol:cluster.name}</h1>
> +
> +<div id="value">${data?.value}</div>
> +
> +<t:eventlink event="clear">Clear</t:eventlink>
> +
> +<t:eventlink event="createValue" context="'value1'">create1</t:eventlink>
> +<t:eventlink event="createValue" context="'value2'">create2</t:eventlink>
> +
> +<t:eventlink event="changeValue" context="'value3'">update1</t:eventlink>
> +<t:eventlink event="changeValue" context="'value4'">update2</t:eventlink>
> +
> +</body>
> +
> +</html>
> \ No newline at end of file
>
>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org