You are viewing a plain text version of this content. The canonical link for it is here.
Posted to wadi-commits@incubator.apache.org by bd...@apache.org on 2005/12/14 23:36:16 UTC

svn commit: r356933 [12/35] - in /incubator/wadi/trunk: ./ etc/ modules/ modules/assembly/ modules/assembly/src/ modules/assembly/src/bin/ modules/assembly/src/conf/ modules/assembly/src/main/ modules/assembly/src/main/assembly/ modules/core/ modules/c...

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/JkRouter.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/JkRouter.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/JkRouter.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/JkRouter.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,150 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.wadi.InvocationContext;
+import org.codehaus.wadi.Router;
+import org.codehaus.wadi.RouterConfig;
+
+public class JkRouter implements Router {
+	
+	protected final Log _log=LogFactory.getLog(getClass());
+	protected final String _info;
+	protected final String _suffix;
+	
+	public JkRouter(String info) {
+		_info=info;
+		_suffix="."+_info;
+	}
+	
+	protected RouterConfig _config;
+	
+	public void init(RouterConfig config) {
+		_config=config;
+	}
+	
+	public void destroy() {
+		_config=null;
+	}
+	
+	public String strip(String session) {
+		int i=session.lastIndexOf(".");
+		if (i<0)
+			return session;
+		else
+			return session.substring(0, i);
+	}
+	
+	public String augment(String id) {
+		return augment(id, _suffix);
+	}
+	
+	public String augment(String id, String target) {
+		assert id!=null;
+		assert target.startsWith(".");
+		
+		int i=id.lastIndexOf(".");
+		if (i<0) // id has no routing info
+			return id+target;
+		else // routing info already present
+			if (id.endsWith(target))
+				return id; // it's our routing info - leave it
+			else
+				return id.substring(0, i)+target; // it's someone else's - replace it
+		
+	}
+	
+	public String getInfo() {
+		return _info;
+	}
+	
+	public boolean canReroute() {
+		return true;
+	}
+	
+	public boolean reroute(InvocationContext invocationContext) {
+		WebInvocationContext context = (WebInvocationContext) invocationContext;
+		HttpServletRequest req = context.getHreq();
+		HttpServletResponse res = context.getHres();
+		String id=req.getRequestedSessionId();
+		
+		if (id.endsWith(_suffix))
+			return false;
+		
+		if (req.isRequestedSessionIdFromCookie())
+			return rerouteCookie(req, res, id);
+		else
+			return false;
+	}
+	
+	public boolean rerouteCookie(HttpServletRequest req, HttpServletResponse res, String id) {
+		return rerouteCookie(req, res, id, _suffix);
+	}
+	
+	public boolean rerouteCookie(HttpServletRequest req, HttpServletResponse res, String id, String target) {
+		assert target.startsWith(".");
+		
+		String oldId=id;
+		String newId=augment(id);
+		
+		if (_log.isInfoEnabled()) _log.info("rerouting cookie: " + oldId + " -> " + newId);
+		
+		Cookie[] cookies=req.getCookies();
+		
+		// TODO - what about case sensitivity on value ?
+		for (int i=0;i<cookies.length;i++)
+		{
+			Cookie cookie=cookies[i];
+			if (cookie.getName().equalsIgnoreCase(_config.getSessionCookieName()) && cookie.getValue().equals(oldId))
+			{
+				// name, path and domain must match those on client side,
+				// for cookie to be updated in browser...
+				
+				String cookiePath=_config.getSessionCookiePath(req);
+				if (cookiePath!=null)
+					cookie.setPath(cookiePath);
+				
+				String cookieDomain=_config.getSessionCookieDomain();
+				if (cookieDomain!=null)
+					cookie.setDomain(cookieDomain);
+				
+				cookie.setValue(newId); // the session id with redirected routing info
+				
+				res.addCookie(cookie);
+			}
+		}
+		
+		return false;
+	}
+	
+	public boolean rerouteURL() {
+		return rerouteURL(_suffix);
+	}
+	
+	public boolean rerouteURL(String target) {
+		assert target.startsWith(".");
+		// TODO Auto-generated method stub
+		return false;
+	}
+	
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyAttributes.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyAttributes.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyAttributes.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyAttributes.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,122 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.wadi.AttributesConfig;
+
+public class LazyAttributes extends DistributableAttributes {
+
+    public LazyAttributes(AttributesConfig config, Map map) {
+        super(config, map);
+        // TODO Auto-generated constructor stub
+    }
+    protected static final Log _log = LogFactory.getLog(LazyAttributes.class);
+
+    protected transient byte[] _bytes;
+
+    protected void deserialise() {
+        try {
+            // deserialise content at last minute ...
+            ByteArrayInputStream bais=new ByteArrayInputStream(_bytes);
+            ObjectInputStream ois=new ObjectInputStream(bais);
+            super.readContent(ois);
+            ois.close();
+        } catch (Exception e) {
+	  _log.error("unexpected problem lazily deserialising session attribute value - data lost", e);
+        } finally {
+            _bytes=null;
+        }
+    }
+
+    protected void serialise() throws IOException {
+        ByteArrayOutputStream baos=new ByteArrayOutputStream(); // TODO - pool these objects...
+        ObjectOutputStream oos=new ObjectOutputStream(baos);
+        super.writeContent(oos);
+        oos.close();
+        _bytes=baos.toByteArray();
+    }
+
+    public synchronized Object get(Object key) {
+        if (_bytes!=null)
+            deserialise();
+
+        return super.get(key);
+    }
+
+    public Object remove(Object key) {
+        if (_bytes!=null)
+            deserialise();
+        return super.remove(key);
+    }
+
+    public Object put(Object key, Object newValue) {
+        if (_bytes!=null)
+            deserialise();
+         return super.put(key, newValue);
+    }
+
+    public int size() {
+        if (_bytes!=null)
+            deserialise();
+        return super.size(); // TODO - this should not need deserialisation...
+    }
+
+    public Set keySet(){
+        if (_bytes!=null)
+            deserialise();
+        return super.keySet();
+    }
+
+    public void clear(){
+        _bytes=null;
+    }
+
+    public Set getListenerNames() {
+        if (_bytes!=null)
+            deserialise();
+        return _listenerNames;
+    }
+
+    public synchronized void writeContent(ObjectOutput oo) throws IOException {
+        if (_bytes==null)
+            serialise(); // rebuild cache
+
+        oo.writeInt(_bytes.length);
+        oo.write(_bytes);
+    }
+
+    public synchronized void readContent(ObjectInput oi) throws IOException, ClassNotFoundException {
+        int length=oi.readInt();
+        _bytes=new byte[length];
+        if (oi.read(_bytes)!=length)
+            throw new IOException("data truncated whilst reading Session Attributes- data lost");
+        _map.clear();
+    }
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyAttributesFactory.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyAttributesFactory.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyAttributesFactory.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyAttributesFactory.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,31 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.util.HashMap;
+
+import org.codehaus.wadi.Attributes;
+import org.codehaus.wadi.AttributesConfig;
+import org.codehaus.wadi.AttributesFactory;
+
+public class LazyAttributesFactory implements AttributesFactory {
+
+    public Attributes create(AttributesConfig config) {
+        return new LazyAttributes(config, new HashMap());
+    }
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyHttpSessionBindingEvent.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyHttpSessionBindingEvent.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyHttpSessionBindingEvent.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyHttpSessionBindingEvent.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,56 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionBindingEvent;
+
+// IDEA - implement later
+
+// With an event like this, even if an HttpSessionAttributeListener is registered with the Context
+// we should be able to avoid deserialisation of a LazyValue, even on session invalidation, unless
+// the LazyValue's value is actually referenced through the event. This would be a substantial saving.
+
+// The same should be possible for LazyAttributes - the first direct reference through the event would
+// deserialise all attribute Values. This will involve changing the way that lazyAttribute serialises.
+// it should probably write out numAttributes, then foreach attribute: listener? and key then foreach
+// attribute: value... etc..
+
+// Ultimately, if session metadata encodes whether a session is carrying binding or activation listeners
+// it should be possible to e.g. expire sessions without listeners directly on disc without reloading (unless
+// listeners are registered with the Context). This would also be a big win.
+
+// I think we will have to change the Value.setValue() sig to include the attribute Name, so that events can be
+// raised from this point. We will also probably have to collapse the BindingNotification aspect into the Value class
+// so that we can keep taks on which attributes are listeners and which are not.
+
+public class LazyHttpSessionBindingEvent extends HttpSessionBindingEvent {
+
+    public LazyHttpSessionBindingEvent(HttpSession session, String name) {
+        super(session, name);
+    }
+
+    public LazyHttpSessionBindingEvent(HttpSession session, String name, Object value) {
+        super(session, name, value);
+    }
+    
+    public Object getValue() {
+        LazyValue value=(LazyValue)super.getValue();
+        return value==null?null:value.getValue();
+    }
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyValue.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyValue.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyValue.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyValue.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,106 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+
+import javax.servlet.http.HttpSessionActivationListener;
+import javax.servlet.http.HttpSessionBindingListener;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.wadi.DistributableValueConfig;
+
+public class LazyValue extends DistributableValue {
+    protected static final Log _log = LogFactory.getLog(LazyValue.class);
+
+    protected transient boolean _listener;
+    protected transient byte[] _bytes;
+
+    public LazyValue(DistributableValueConfig config) {
+        super(config);
+    }
+
+    protected void deserialise() {
+        try {
+            // deserialise content at last minute ...
+            ByteArrayInputStream bais=new ByteArrayInputStream(_bytes);
+            ObjectInputStream ois=new ObjectInputStream(bais);
+            super.readContent(ois);
+            ois.close();
+        } catch (Exception e) {
+	  _log.error("unexpected problem lazily deserialising session attribute value - data lost", e);
+        } finally {
+            _bytes=null;
+        }
+    }
+
+    protected void serialise() throws IOException {
+        ByteArrayOutputStream baos=new ByteArrayOutputStream(); // TODO - pool these objects...
+        ObjectOutputStream oos=new ObjectOutputStream(baos);
+        super.writeContent(oos);
+        oos.close();
+        _bytes=baos.toByteArray();
+    }
+
+    public synchronized Object getValue() {
+        if (_bytes!=null)
+            deserialise();
+
+        return super.getValue();
+    }
+
+    public synchronized Object setValue(Object newValue) {
+        if (_bytes!=null) {
+            if (_listener || ((DistributableValueConfig)_config).getHttpSessionAttributeListenersRegistered())
+                deserialise(); // oldValue needs deserialising before it is chucked...
+        }
+
+        Object tmp=super.setValue(newValue);
+        _listener=(_value instanceof HttpSessionActivationListener) || (_value instanceof HttpSessionBindingListener); // doubles up on test in super...
+        return tmp;
+    }
+
+    public synchronized void writeContent(ObjectOutput oo) throws IOException {
+        if (_bytes==null)
+            serialise(); // rebuild cache
+
+        oo.writeBoolean(_listener);
+        oo.writeInt(_bytes.length);
+        oo.write(_bytes);
+    }
+
+    public synchronized void readContent(ObjectInput oi) throws IOException, ClassNotFoundException {
+        _listener=oi.readBoolean();
+        int length=oi.readInt();
+        _bytes=new byte[length];
+        if (oi.read(_bytes)!=length)
+            throw new IOException("data truncated whilst reading Session attribute value - data lost");
+        _value=null;
+    }
+
+    public boolean isListener(){return _listener;}
+
+    // should we register Listeners with our Attributes ?
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyValueFactory.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyValueFactory.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyValueFactory.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/LazyValueFactory.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,34 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import org.codehaus.wadi.DistributableValueConfig;
+import org.codehaus.wadi.Value;
+import org.codehaus.wadi.ValueConfig;
+import org.codehaus.wadi.ValueFactory;
+
+public class LazyValueFactory implements ValueFactory {
+
+    public LazyValueFactory() {
+        super();
+    }
+
+    public Value create(ValueConfig config) {
+        return new LazyValue((DistributableValueConfig)config);
+    }
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/ListenerSupport.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/ListenerSupport.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/ListenerSupport.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/ListenerSupport.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,82 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.util.ArrayList;
+import java.util.EventListener;
+import java.util.List;
+
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class ListenerSupport {
+
+	protected final Log _log = LogFactory.getLog(getClass());
+	protected final List _sessionListeners = new ArrayList();
+	protected final List _attributeListeners = new ArrayList();
+
+	public synchronized void addEventListener(EventListener listener) throws IllegalArgumentException, IllegalStateException {
+//		if (isStarted())
+//			throw new IllegalStateException("EventListeners must be added before a Session Manager starts");
+
+		boolean known=false;
+		if (listener instanceof HttpSessionAttributeListener)
+		{
+			if (_log.isDebugEnabled()) _log.debug("adding HttpSessionAttributeListener: "+listener);
+			_attributeListeners.add(listener);
+			known=true;
+		}
+		if (listener instanceof HttpSessionListener)
+		{
+			if (_log.isDebugEnabled()) _log.debug("adding HttpSessionListener: "+listener);
+			_sessionListeners.add(listener);
+			known=true;
+		}
+
+		if (!known)
+			throw new IllegalArgumentException("Unknown EventListener type "+listener);
+	}
+
+	public synchronized void removeEventListener(EventListener listener) throws IllegalStateException {
+//		if (isStarted())
+//			throw new IllegalStateException("EventListeners may not be removed while a Session Manager is running");
+
+		boolean known=false;
+		if (listener instanceof HttpSessionAttributeListener)
+		{
+			if (_log.isDebugEnabled()) _log.debug("removing HttpSessionAttributeListener: "+listener);
+			known|=_attributeListeners.remove(listener);
+		}
+		if (listener instanceof HttpSessionListener)
+		{
+			if (_log.isDebugEnabled()) _log.debug("removing HttpSessionListener: "+listener);
+			known|=_sessionListeners.remove(listener);
+		}
+
+		if (!known)
+			if (_log.isWarnEnabled()) _log.warn("EventListener not registered: "+listener);
+	}
+
+	public void installListeners(StandardManager manager) {
+		manager.setSessionListeners((HttpSessionListener[])_sessionListeners.toArray(new HttpSessionListener[_sessionListeners.size()]));
+		manager.setAttributelisteners((HttpSessionAttributeListener[])_attributeListeners.toArray(new HttpSessionAttributeListener[_attributeListeners.size()]));
+	}
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MBeanServerFactoryBean.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MBeanServerFactoryBean.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MBeanServerFactoryBean.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MBeanServerFactoryBean.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,57 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.util.ArrayList;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+
+// only create a new MBeanServer if one does not already exist...
+
+public class MBeanServerFactoryBean extends org.springframework.jmx.support.MBeanServerFactoryBean {
+
+	protected MBeanServer _server;
+
+	public void afterPropertiesSet() {
+		String agentId=null; // TODO - parameterise
+		ArrayList servers=MBeanServerFactory.findMBeanServer(agentId);
+		if (servers!=null && servers.size()>0)
+			_server=(MBeanServer)servers.get(0);
+		else
+			super.afterPropertiesSet();
+	}
+
+	public Object getObject() {
+		if (_server!=null)
+			return _server;
+		else
+			return super.getObject();
+	}
+
+	public Class getObjectType() {
+		if (_server!=null)
+			return _server.getClass();
+		else
+			return super.getObjectType();
+	}
+
+	public void destroy() {
+		if (_server==null)
+			super.destroy();
+	}
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryContextualiser.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryContextualiser.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryContextualiser.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryContextualiser.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,201 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.wadi.Context;
+import org.codehaus.wadi.ContextPool;
+import org.codehaus.wadi.Contextualiser;
+import org.codehaus.wadi.Emoter;
+import org.codehaus.wadi.Evictable;
+import org.codehaus.wadi.Evicter;
+import org.codehaus.wadi.Immoter;
+import org.codehaus.wadi.InvocationContext;
+import org.codehaus.wadi.InvocationException;
+import org.codehaus.wadi.Motable;
+import org.codehaus.wadi.PoolableInvocationWrapper;
+import org.codehaus.wadi.PoolableInvocationWrapperPool;
+import org.codehaus.wadi.Streamer;
+
+import EDU.oswego.cs.dl.util.concurrent.Sync;
+import EDU.oswego.cs.dl.util.concurrent.TimeoutException;
+
+/**
+ * A Contextualiser that stores its state in Memory as Java Objects
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.9 $
+ */
+public class MemoryContextualiser extends AbstractExclusiveContextualiser {
+	protected final ContextPool _pool;
+	protected final Streamer _streamer;
+	protected final Immoter _immoter;
+	protected final Emoter _emoter;
+	protected final Emoter _evictionEmoter;
+	protected final PoolableInvocationWrapperPool _requestPool;
+	protected final Log _lockLog=LogFactory.getLog("org.codehaus.wadi.LOCKS");
+
+	public MemoryContextualiser(Contextualiser next, Evicter evicter, Map map, Streamer streamer, ContextPool pool, PoolableInvocationWrapperPool requestPool) {
+		super(next, new RWLocker(), false, evicter, map);
+		_pool=pool;
+
+		// TODO - streamer should be used inside Motables get/setBytes() methods  but that means a ref in every session :-(
+		_streamer=streamer;
+
+		_immoter=new MemoryImmoter(_map);
+		_emoter=new MemoryEmoter(_map);
+		_evictionEmoter=new AbstractMappedEmoter(_map){public String getInfo(){return "memory";}};
+
+		_requestPool=requestPool;
+	}
+
+	public boolean isExclusive(){return true;}
+
+	// TODO - sometime figure out how to make this a wrapper around AbstractMappedContextualiser.handle() instead of a replacement...
+	public boolean handle(InvocationContext invocationContext, String id, Immoter immoter, Sync motionLock) throws InvocationException {
+		Motable emotable=get(id);
+		if (emotable==null)
+			return false; // we cannot proceed without the session...
+
+		if (immoter!=null) {
+			return promote(invocationContext, id, immoter, motionLock, emotable); // motionLock will be released here...
+		} else {
+			return contextualiseLocally(invocationContext, id, motionLock, emotable);
+		}
+	}
+
+	public boolean contextualiseLocally(InvocationContext invocationContext, String id, Sync invocationLock, Motable motable)  throws InvocationException {
+		Sync stateLock=((Context)motable).getSharedLock();
+		boolean stateLockAcquired=false;
+		try {
+			try {
+				Utils.acquireUninterrupted("State (shared)", id, stateLock);
+				stateLockAcquired=true;
+			} catch (TimeoutException e) {
+				if (_log.isErrorEnabled()) _log.error("unexpected timeout - continuing without lock: "+id+" : "+stateLock, e);
+				// give this some more thought - TODO
+			}
+
+			if (motable.getName()==null) {
+				if (_log.isTraceEnabled()) _log.trace("context disappeared whilst we were waiting for lock: "+id+" : "+stateLock);
+				return false; // retain the InvocationLock, release the StateLock
+			}
+
+			if (invocationLock!=null) {
+				Utils.release("Invocation", id, invocationLock);
+			}
+
+
+			motable.setLastAccessedTime(System.currentTimeMillis());
+			if (false == invocationContext.isProxiedInvocation()) {
+				// part of the proxying proedure runs a null req...
+				// restick clients whose session is here, but whose routing info points elsewhere...
+				_config.getRouter().reroute(invocationContext); // TODO - hmm... still thinking
+				// take wrapper from pool...
+				PoolableInvocationWrapper wrapper = _requestPool.take();
+				wrapper.init(invocationContext, (Context)motable);
+				invocationContext.invoke(wrapper);
+				wrapper.destroy();
+				_requestPool.put(wrapper);
+			} else {
+				invocationContext.invoke();
+			}
+			return true;
+		} finally {
+			if (stateLockAcquired) {
+				Utils.release("State (shared)", id, stateLock);
+			}
+		}
+	}
+
+	class MemoryEmoter extends AbstractMappedEmoter {
+
+		public MemoryEmoter(Map map) {
+			super(map);
+		}
+
+		public boolean prepare(String name, Motable emotable, Motable immotable) {
+			Sync stateLock=((Context)emotable).getExclusiveLock();
+			try {
+				Utils.acquireUninterrupted("State (excl.)", name, stateLock); // released in commit/rollback
+			} catch (TimeoutException e) {
+				_log.error("unexpected timeout", e);
+				return false;
+			}
+
+			if (emotable.getName()==null)
+				return false; // we lost race to motable and it has gone...
+
+			// copies emotable content into immotable
+			return super.prepare(name, emotable, immotable);
+		}
+
+		public void commit(String name, Motable emotable) {
+			// removes emotable from map
+			// destroys emotable
+			super.commit(name, emotable);
+			Sync stateLock=((Context)emotable).getExclusiveLock();
+			Utils.release("State (excl.)", name, stateLock);
+		}
+
+		public void rollback(String name, Motable emotable) {
+			// noop
+			super.rollback(name, emotable);
+			Sync stateLock=((Context)emotable).getExclusiveLock();
+			Utils.release("State (excl.)", name, stateLock);
+		}
+
+		public String getInfo() {
+			return "memory";
+		}
+	}
+
+	class MemoryImmoter extends AbstractMappedImmoter {
+
+		public MemoryImmoter(Map map) {
+			super(map);
+		}
+
+		public Motable nextMotable(String id, Motable emotable) {
+			return _pool.take();
+		}
+
+		public boolean contextualise(InvocationContext invocationContext, String id, Motable immotable, Sync motionLock) throws InvocationException {
+			return contextualiseLocally(invocationContext, id, motionLock, immotable);
+		}
+
+		public String getInfo() {
+			return "memory";
+		}
+	}
+
+	public Immoter getImmoter(){return _immoter;}
+	public Emoter getEmoter(){return _emoter;}
+	public Immoter getPromoter(Immoter immoter) {return immoter==null?_immoter:immoter;}
+
+	public Sync getEvictionLock(String id, Motable motable){return ((Context)motable).getExclusiveLock();}
+	public Emoter getEvictionEmoter(){return _evictionEmoter;} // leave lock-taking to evict()...
+
+	public void setLastAccessTime(Evictable evictable, long oldTime, long newTime) {_evicter.setLastAccessedTime(evictable, oldTime, newTime);}
+	public void setMaxInactiveInterval(Evictable evictable, int oldInterval, int newInterval) {_evicter.setMaxInactiveInterval(evictable, oldInterval, newInterval);}
+
+	public void expire(Motable motable) {_config.expire(motable);}
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryReplicater.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryReplicater.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryReplicater.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryReplicater.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,83 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.util.Map;
+
+import org.activecluster.Node;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.wadi.Replicater;
+
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
+
+public class MemoryReplicater implements Replicater {
+	
+	protected final Log _log = LogFactory.getLog(getClass());
+	
+	protected int numReplicants;
+	
+	public MemoryReplicater(int numReplicants) {
+	}
+	
+	// client part
+	
+	public void create(Object tmp) {
+		if (_log.isTraceEnabled()) _log.trace("create: " + tmp);
+		// decide on replication partners (not ourselves)
+		// sned messages (sync) to insert replicants on these partners
+	}
+	
+	public void update(Object tmp) {
+		if (_log.isTraceEnabled()) _log.trace("update: " + tmp);
+		// send messages (sync) to replicate to our partners
+	}
+	
+	public void destroy(Object tmp) {
+		if (_log.isTraceEnabled()) _log.trace("destroy: " + tmp);
+		// send messages (sync) to remove replicants on partners
+	}
+	
+	public boolean getReusingStore() {
+		return false;
+	}
+	
+	// server part
+	protected Map _replicants=new ConcurrentHashMap();
+	
+	public void insert(String key, Object tmp) {
+		if (_log.isTraceEnabled()) _log.trace("insert: " + key);
+		_replicants.put(key, tmp);
+	}
+	
+	public void replicate(String key, Object value) {
+		if (_log.isTraceEnabled()) _log.trace("replicate: " + key);
+	}
+	
+	public void remove(String key) {
+		if (_log.isTraceEnabled()) _log.trace("remove: " + key);
+	}
+	
+	// restore
+	
+	public void nodeDied(Node node) {
+		// the partitions owned by this node will be reconstructed and repopulated with session-key:location pairs
+		// we need to know which sessions this node was owner of when it died - partition owners will know
+		// these sessions may then be promoted to memory in our node and the partition owners will need to be updated as to their new location.
+	}
+	
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryReplicaterFactory.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryReplicaterFactory.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryReplicaterFactory.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/MemoryReplicaterFactory.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,41 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import org.codehaus.wadi.Replicater;
+import org.codehaus.wadi.ReplicaterFactory;
+
+/**
+ * MemoryReplicaters hold per Session state (the location of their replication partners), so we need to create a new
+ * MemoryReplicater for each session.
+ *
+ * @author jules
+ *
+ */
+public class MemoryReplicaterFactory implements ReplicaterFactory {
+
+	protected int _numReplicants;
+
+	public MemoryReplicaterFactory(int numReplicants) {
+		_numReplicants=numReplicants;
+	}
+
+	public Replicater create() {
+		return new MemoryReplicater(_numReplicants);
+	}
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/NeverEvicter.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/NeverEvicter.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/NeverEvicter.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/NeverEvicter.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,35 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import org.codehaus.wadi.Evictable;
+
+/**
+ * An Evicter which never evicts (except explicitly invalidated sessions).
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.1 $
+ */
+public class NeverEvicter extends AbstractBestEffortEvicter {
+
+    public NeverEvicter(int sweepInterval, boolean strictOrdering) {
+        super(sweepInterval, strictOrdering);
+    }
+    
+    public boolean test(Evictable evictable, long time, long ttl) {return false;}
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/ObjectInputStream.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/ObjectInputStream.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/ObjectInputStream.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/ObjectInputStream.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,108 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+
+public class ObjectInputStream extends java.io.ObjectInputStream {
+
+	protected final ClassLoader _classLoader;
+
+	public ObjectInputStream(InputStream is, ClassLoader classLoader) throws IOException {
+		super(is);
+		_classLoader=classLoader;
+	}
+
+	// copied from super as this seems to be the only way to parameterise the ClassLoader... - TODO
+
+	/*
+	 * @(#)ObjectInputStream.java	1.146 04/01/13
+	 *
+	 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+	 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+	 */
+	private static final HashMap primClasses = new HashMap(8, 1.0F);
+	static {
+		primClasses.put("boolean", boolean.class);
+		primClasses.put("byte", byte.class);
+		primClasses.put("char", char.class);
+		primClasses.put("short", short.class);
+		primClasses.put("int", int.class);
+		primClasses.put("long", long.class);
+		primClasses.put("float", float.class);
+		primClasses.put("double", double.class);
+		primClasses.put("void", void.class);
+	}
+
+    protected Class resolveClass(ObjectStreamClass desc)
+	throws IOException, ClassNotFoundException
+	{
+		String name = desc.getName();
+		try {
+			return Class.forName(name, false, _classLoader);
+		} catch (ClassNotFoundException ex) {
+			Class cl = (Class) primClasses.get(name);
+			if (cl != null) {
+				return cl;
+			} else {
+				throw ex;
+			}
+		}
+	}
+
+    protected Class resolveProxyClass(String[] interfaces)
+	throws IOException, ClassNotFoundException
+	{
+		ClassLoader latestLoader = _classLoader;
+		ClassLoader nonPublicLoader = null;
+		boolean hasNonPublicInterface = false;
+
+		// define proxy in class loader of non-public interface(s), if any
+		Class[] classObjs = new Class[interfaces.length];
+		for (int i = 0; i < interfaces.length; i++) {
+			Class cl = Class.forName(interfaces[i], false, latestLoader);
+			if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {
+				if (hasNonPublicInterface) {
+					if (nonPublicLoader != cl.getClassLoader()) {
+						throw new IllegalAccessError(
+						"conflicting non-public interface class loaders");
+					}
+				} else {
+					nonPublicLoader = cl.getClassLoader();
+					hasNonPublicInterface = true;
+				}
+			}
+			classObjs[i] = cl;
+		}
+		try {
+			return Proxy.getProxyClass(
+					hasNonPublicInterface ? nonPublicLoader : latestLoader,
+							classObjs);
+		} catch (IllegalArgumentException e) {
+			throw new ClassNotFoundException(null, e);
+		}
+	}
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/PartiallyReplicableSession.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/PartiallyReplicableSession.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/PartiallyReplicableSession.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/PartiallyReplicableSession.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,36 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import org.codehaus.wadi.ReplicableSessionConfig;
+
+// A Session from which we can generate replication deltas, instead of complete backup copies...
+
+// I think this approach is only possible under the assumption of ByValue Semantics.
+
+public class PartiallyReplicableSession extends AbstractReplicableSession {
+
+	public PartiallyReplicableSession(ReplicableSessionConfig config) {
+		super(config);
+		// NYI
+	}
+
+	public void readEnded() {
+		throw new UnsupportedOperationException("NYI");
+	}
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/Quipu.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/Quipu.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/Quipu.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/Quipu.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,65 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import EDU.oswego.cs.dl.util.concurrent.WaitableInt;
+
+/**
+ * You have a flock of n Llamas, you [un]tie a knot in your Quipu as each one leaves/enters your pen.
+ * When all are in/out, you are free to continue. If the Llamas take too long, you can leave anyway !
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.2 $
+ */
+public class Quipu extends WaitableInt {
+
+	protected final static Log _log = LogFactory.getLog(Quipu.class);
+	
+	public Quipu(int numLlammas) {
+        super(numLlammas);
+        // TODO Auto-generated constructor stub
+    }
+
+    public boolean waitFor(long timeout) throws InterruptedException {
+        long end=System.currentTimeMillis()+timeout;
+        long now=0;
+        synchronized(lock_) {
+          while (!(value_==0) && (now=System.currentTimeMillis())<end) lock_.wait(end-now);
+        }
+        return value_==0;
+      }
+    
+    // TODO - consider synchronisation...
+    protected final Collection _results=Collections.synchronizedCollection(new ArrayList());
+    
+    public void putResult(Object result) {
+        _results.add(result);
+        if (_log.isTraceEnabled()) _log.trace("result arrived: "+result);
+        decrement();
+    }
+    
+    public Collection getResults() {
+        return _results;
+    }
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RWLock.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RWLock.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RWLock.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RWLock.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,493 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.
+ */
+
+/*
+  File: WriterPreferenceReadWriteLock.java
+
+  Originally written by Doug Lea and released into the public domain.
+  This may be used for any purposes whatsoever without acknowledgment.
+  Thanks for the assistance and support of Sun Microsystems Labs,
+  and everyone contributing, testing, and using this code.
+
+  History:
+  Date       Who                What
+  11Jun1998  dl               Create public version
+   5Aug1998  dl               replaced int counters with longs
+  25aug1998  dl               record writer thread
+   3May1999  dl               add notifications on interrupt/timeout
+
+*/
+
+package org.codehaus.wadi.impl;
+
+// This started off life as a straight copy of Doug Lea's
+// WriterPreferenceReadWriteLock in
+// EDU.oswego.cs.dl.util.concurrent... I will be refactoring it to add
+// priority ordering of writers and lock overlapping...
+
+// Doug's code is under whatever license he chose, mine is under ASF2
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.wadi.RWLockListener;
+
+/**
+ * A read-write lock. Writers are preferred. Writers are ordered
+ * according to 'priority'. A Reader may overlap release of its read
+ * lock with its application for a write lock. A Writer may downgrade,
+ * becoming a Reader.
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.5 $
+ */
+public class RWLock implements ReadWriteLock {
+  protected static final Log _log=LogFactory.getLog(RWLock.class);
+
+  protected int         _maxPriority=Thread.MAX_PRIORITY;
+  protected static ThreadLocal _priority=new ThreadLocal(){protected synchronized Object initialValue() {return new Integer(0);}};
+  protected class Lock {int _count=0;}
+
+  protected long activeReaders_ = 0;
+  protected Thread activeWriter_ = null;
+  protected long waitingReaders_ = 0;
+  protected long waitingWriters_ = 0;
+
+  public RWLock(int maxPriority){_maxPriority=maxPriority;}
+
+  public static void setPriority(int priority){_priority.set(new Integer(priority));}
+
+  public static int getPriority() {
+      int tmp=((Integer)_priority.get()).intValue();
+      if (0==tmp && _log.isWarnEnabled())
+	_log.warn("no thread priority specified", new Exception());
+      return tmp;
+  }
+
+  protected RWLockListener _listener; // cheaper than an array of Listeners and we only need 0-1 (currently)
+
+  public void setListener(RWLockListener listener) {
+	  _listener=listener;
+  }
+
+  protected final ReaderLock readerLock_ = new ReaderLock();
+  protected final WriterLock writerLock_ = new WriterLock();
+
+  public Sync writeLock() { return writerLock_; }
+  public Sync readLock() { return readerLock_; }
+
+  /*
+    A bunch of small synchronized methods are needed
+    to allow communication from the Lock objects
+    back to this object, that serves as controller
+  */
+
+
+  protected synchronized void cancelledWaitingReader() { --waitingReaders_; assert waitingReaders_>-1;}
+  protected synchronized void cancelledWaitingWriter(Lock l) { --waitingWriters_; l._count--; assert waitingWriters_>-1;}
+
+
+  protected boolean allowReader() {
+    return activeWriter_ == null;
+  }
+
+
+  protected synchronized boolean startRead() {
+    boolean allowRead = allowReader();
+    if (allowRead)  ++activeReaders_;
+    return allowRead;
+  }
+
+  protected synchronized boolean startWrite() {
+
+    // The allowWrite expression cannot be modified without
+    // also changing startWrite, so is hard-wired
+
+    boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0);
+    if (allowWrite)  activeWriter_ = Thread.currentThread();
+    return allowWrite;
+   }
+
+
+  /*
+     Each of these variants is needed to maintain atomicity
+     of wait counts during wait loops. They could be
+     made faster by manually inlining each other. We hope that
+     compilers do this for us though.
+  */
+
+  protected synchronized boolean startReadFromNewReader() {
+    boolean pass = startRead();
+    if (!pass) ++waitingReaders_;
+    return pass;
+  }
+
+  protected synchronized boolean startWriteFromNewWriter(Lock l) {
+    boolean pass = startWrite();
+    if (!pass)
+    {
+      ++waitingWriters_;
+      l._count++;
+    }
+    return pass;
+  }
+
+  protected synchronized boolean startReadFromWaitingReader() {
+    boolean pass = startRead();
+    if (pass)
+    {
+      --waitingReaders_;
+      assert waitingReaders_>-1;
+    }
+
+    return pass;
+  }
+
+  protected synchronized boolean startWriteFromWaitingWriter(Lock l) {
+    boolean pass = startWrite();
+    if (pass)
+    {
+      --waitingWriters_;
+      assert waitingWriters_>-1;
+      l._count--;
+    }
+    return pass;
+  }
+
+  protected boolean notifyReadEnded() {
+	  if (_listener!=null)
+		  _listener.readEnded();
+
+	  return true;
+  }
+
+  /**
+   * Called upon termination of a read.
+   * Returns the object to signal to wake up a waiter, or null if no such
+   **/
+  protected synchronized Signaller endRead() {
+    if ((--activeReaders_==0 && notifyReadEnded()) && waitingWriters_ > 0)
+      return writerLock_;
+    else
+    {
+      if (_log.isTraceEnabled())
+	_log.trace("activeReaders_="+activeReaders_);
+      assert activeReaders_>-1;
+      return null;
+    }
+  }
+
+
+  /**
+   * Called upon termination of a write.
+   * Returns the object to signal to wake up a waiter, or null if no such
+   **/
+  protected synchronized Signaller endWrite() {
+    activeWriter_ = null;
+    if (waitingReaders_ > 0 && allowReader())
+      return readerLock_;
+    else if (waitingWriters_ > 0)
+      return writerLock_;
+    else
+      return null;
+  }
+
+
+  /**
+   * Reader and Writer requests are maintained in two different
+   * wait sets, by two different objects. These objects do not
+   * know whether the wait sets need notification since they
+   * don't know preference rules. So, each supports a
+   * method that can be selected by main controlling object
+   * to perform the notifications.  This base class simplifies mechanics.
+   **/
+
+  protected abstract class Signaller  { // base for ReaderLock and WriterLock
+    abstract void signalWaiters();
+  }
+
+  protected class ReaderLock extends Signaller implements Sync {
+
+  public String
+    toString()
+  {
+    return "<RWLock.ReaderLock:"+ReaderLock.this.hashCode()+":"+Thread.currentThread()+":activeReaders:"+activeReaders_+", waitingReaders:"+waitingReaders_+">";
+  }
+
+    public  void acquire() throws InterruptedException {
+      if (_log.isTraceEnabled())
+	_log.trace(Thread.currentThread().toString()+" acquiring R-lock "+RWLock.this.hashCode());//, new Exception());
+      if (Thread.interrupted()) throw new InterruptedException();
+      InterruptedException ie = null;
+      synchronized(this) {
+        if (!startReadFromNewReader()) {
+          for (;;) {
+            try {
+              ReaderLock.this.wait();
+              if (startReadFromWaitingReader())
+                return;
+            }
+            catch(InterruptedException ex){
+              cancelledWaitingReader();
+              ie = ex;
+              break;
+            }
+          }
+        }
+      }
+      if (ie != null) {
+        // fall through outside synch on interrupt.
+        // This notification is not really needed here,
+        //   but may be in plausible subclasses
+        writerLock_.signalWaiters();
+        throw ie;
+      }
+    }
+
+
+    public void release() {
+      if (_log.isTraceEnabled())
+	_log.trace(Thread.currentThread().toString()+" releasing R-lock "+RWLock.this.hashCode());//, new Exception());
+      Signaller s = endRead();
+      if (s != null) s.signalWaiters();
+    }
+
+
+    synchronized void signalWaiters() { ReaderLock.this.notifyAll(); }
+
+    public boolean attempt(long msecs) throws InterruptedException {
+      if (_log.isTraceEnabled())
+	_log.trace(Thread.currentThread().toString()+" attempting R-lock "+RWLock.this.hashCode());//, new Exception());
+      if (Thread.interrupted()) throw new InterruptedException();
+      InterruptedException ie = null;
+      synchronized(this) {
+        if (msecs <= 0)
+          return startRead();
+        else if (startReadFromNewReader())
+          return true;
+        else {
+          long waitTime = msecs;
+          long start = System.currentTimeMillis();
+          for (;;) {
+            try { ReaderLock.this.wait(waitTime);  }
+            catch(InterruptedException ex){
+              cancelledWaitingReader();
+              ie = ex;
+              break;
+            }
+            if (startReadFromWaitingReader())
+              return true;
+            else {
+              waitTime = msecs - (System.currentTimeMillis() - start);
+              if (waitTime <= 0) {
+                cancelledWaitingReader();
+                break;
+              }
+            }
+          }
+        }
+      }
+      // safeguard on interrupt or timeout:
+      writerLock_.signalWaiters();
+      if (ie != null) throw ie;
+      else return false; // timed out
+    }
+
+  }
+
+  protected class WriterLock extends Signaller implements  Sync {
+
+    public String
+      toString()
+    {
+      return "<RWLock.WriterLock:"+WriterLock.this.hashCode()+":"+Thread.currentThread()+":"+" activeWriter:"+(activeWriter_!=null)+", waitingWriters:"+waitingWriters_+">";
+    }
+
+    Lock[] _locks=new Lock[_maxPriority+1];
+
+    WriterLock()
+    {
+      for (int i=0;i<=_maxPriority;i++)
+	_locks[i]=new Lock();
+    }
+
+    public void acquire() throws InterruptedException {
+      if (_log.isTraceEnabled())
+	_log.trace(Thread.currentThread().toString()+" acquiring W-lock "+RWLock.this.hashCode());//, new Exception());
+      if (Thread.interrupted()) throw new InterruptedException();
+      InterruptedException ie = null;
+      int p=getPriority();
+      Lock l=_locks[p];
+      synchronized(l) {
+        if (!startWriteFromNewWriter(l)) {
+          for (;;) {
+            try {
+              l.wait();
+              if (startWriteFromWaitingWriter(l))
+                return;
+            }
+            catch(InterruptedException ex){
+              cancelledWaitingWriter(l);
+              l.notify();
+              ie = ex;
+              break;
+            }
+          }
+        }
+      }
+      if (ie != null) {
+        // Fall through outside synch on interrupt.
+        //  On exception, we may need to signal readers.
+        //  It is not worth checking here whether it is strictly necessary.
+        readerLock_.signalWaiters();
+        throw ie;
+      }
+    }
+
+    public void release(){
+      if (_log.isTraceEnabled())
+	_log.trace(Thread.currentThread().toString()+" releasing W-lock "+RWLock.this.hashCode());//, new Exception());
+      Signaller s = endWrite();
+      if (s != null) s.signalWaiters();
+    }
+
+    synchronized void
+      signalWaiters()
+    {
+      // walk down from top priority looking for a thread to notify...
+      for (int i=_maxPriority;i>=0;i--)
+      {
+	Lock l=_locks[i];
+	synchronized (l)
+	{
+	  if (l._count>0)
+	  {
+	    l.notify();
+	    return;
+	  }
+	}
+      }
+    }
+
+    public boolean attempt(long msecs) throws InterruptedException {
+      if (_log.isTraceEnabled())
+	_log.trace(Thread.currentThread().toString()+" attempting W-lock "+RWLock.this.hashCode());//, new Exception());
+      if (Thread.interrupted()) throw new InterruptedException();
+      InterruptedException ie = null;
+      int p=getPriority();
+      Lock l=_locks[p];
+      synchronized(l) {
+        if (msecs <= 0)
+          return startWrite();
+        else if (startWriteFromNewWriter(l))
+          return true;
+        else {
+          long waitTime = msecs;
+          long start = System.currentTimeMillis();
+          for (;;) {
+            try { l.wait(waitTime);  }
+            catch(InterruptedException ex){
+              cancelledWaitingWriter(l);
+              l.notify();
+              ie = ex;
+              break;
+            }
+            if (startWriteFromWaitingWriter(l))
+              return true;
+            else {
+              waitTime = msecs - (System.currentTimeMillis() - start);
+              if (waitTime <= 0) {
+                cancelledWaitingWriter(l);
+		l.notify();
+                break;
+              }
+            }
+          }
+        }
+      }
+
+      readerLock_.signalWaiters();
+      if (ie != null) throw ie;
+      else return false; // timed out
+    }
+
+  }
+
+
+  // Not well tested - I'm concerned about the synchronisation...
+  public void
+    overlap()
+    throws InterruptedException
+  {
+    if (_log.isTraceEnabled())
+      _log.trace(Thread.currentThread().toString()+" overlapping W-lock "+writerLock_.hashCode());
+    synchronized (RWLock.this)
+    {
+      Signaller s=endRead();
+
+      if (s==null)
+      {
+	// there are still extant readers - this call to acquire will
+	// just queue a write lock - it could be optimised but...
+	writeLock().acquire();
+      }
+      else
+      {
+	// readers are exhausted, we don't want to let this writer
+	// jump straight into the gap as it may not be of a higher
+	// priority than the other waiting writers...
+
+	// WARNING - TODO - However, at the moment we KNOW that the
+	// only thread using the overlap method will be the
+	// invalidation request thread so we can live with this -
+	// revisit if we ever need this lock to be used in a different
+	// manner...
+	writeLock().acquire();
+      }
+    }
+  }
+
+  // we are a writer and we want to become a reader (i.e. allow other
+  // readers) without another writer jumping in during the
+  // change-over. Writer preference breaks down here, but this is a
+  // useful ability....
+  public synchronized void
+    downgrade()
+      throws IllegalStateException
+  {
+    // test that we are indeed the current writer....
+    if (activeWriter_!=Thread.currentThread())
+      throw new IllegalStateException("downgrading thread is not current writer");
+    else
+    {
+      // cease being the active writer
+      activeWriter_=null;
+      // become an active reader
+      assert activeReaders_==0;	// we were writing so there should be no active readers...
+      activeReaders_++;
+      // wake waiting readers
+      if (waitingReaders_>0)
+	readerLock_.signalWaiters();
+    }
+  }
+
+  public String
+    toString()
+  {
+    return "<RWLock:"+this.hashCode()+":"+readerLock_+", "+writerLock_+">";
+  }
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RWLocker.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RWLocker.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RWLocker.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RWLocker.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,35 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import org.codehaus.wadi.Context;
+import org.codehaus.wadi.Locker;
+import org.codehaus.wadi.Motable;
+
+import EDU.oswego.cs.dl.util.concurrent.Sync;
+
+public class RWLocker implements Locker {
+
+    public RWLocker() {
+        super();
+    }
+
+    public Sync getLock(String id, Motable motable) {
+        return ((Context)motable).getExclusiveLock();
+    }
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RankedRWLock.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RankedRWLock.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RankedRWLock.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RankedRWLock.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,44 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+// maybe collapse with superclass when sandbox is merged into mainline.
+
+/**
+ * A ReadWriteLock with prioritisable writer threads. The set of priority ranks is tailored
+ * to WADI.
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.1 $
+ */
+
+public class RankedRWLock extends org.codehaus.wadi.impl.RWLock {
+
+    public final static int INVALIDATION_PRIORITY=5; // explicit invalidation by user
+    public final static int EMIGRATION_PRIORITY=4; // session is required on another node
+    public final static int EVICTION_PRIORITY=3; // session is being evicted (implicit timeout)
+    public final static int IMMIGRATION_PRIORITY=2; // TODO - do we need this ?
+    public final static int CREATION_PRIORITY=1; // TODO - do we need this ?
+    public final static int NO_PRIORITY=0; // used to remove any of the above from a thread...
+
+    protected final static int MAX_PRIORITY=INVALIDATION_PRIORITY;
+
+    public RankedRWLock() {
+        super(MAX_PRIORITY);
+    }
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RedirectingRelocater.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RedirectingRelocater.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RedirectingRelocater.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/RedirectingRelocater.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,45 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import org.codehaus.wadi.Contextualiser;
+import org.codehaus.wadi.Immoter;
+import org.codehaus.wadi.InvocationContext;
+import org.codehaus.wadi.InvocationException;
+import org.codehaus.wadi.RequestRelocater;
+
+import EDU.oswego.cs.dl.util.concurrent.Sync;
+
+/**
+ * Relocate the request to its state, by redirecting it to another node.
+ * This is only possible if, for example, the load balancer is using routing information
+ * to decide which node to dispatch stateful requests to. If this is the case, we may be
+ * able rewrite this to achieve our desired effect.
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.2 $
+ */
+public class RedirectingRelocater extends AbstractRelocater implements RequestRelocater {
+	
+	public boolean relocate(InvocationContext invocationContext, String name, Immoter immoter, Sync motionLock) throws InvocationException {
+		return false;
+	}
+	
+	public void setTop(Contextualiser top){/* NYI */}
+	public Contextualiser getTop(){return null;}
+	
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SerialContextualiser.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SerialContextualiser.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SerialContextualiser.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SerialContextualiser.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,101 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.codehaus.wadi.Collapser;
+import org.codehaus.wadi.Context;
+import org.codehaus.wadi.Contextualiser;
+import org.codehaus.wadi.Immoter;
+import org.codehaus.wadi.InvocationContext;
+import org.codehaus.wadi.InvocationException;
+
+import EDU.oswego.cs.dl.util.concurrent.NullSync;
+import EDU.oswego.cs.dl.util.concurrent.Sync;
+import EDU.oswego.cs.dl.util.concurrent.TimeoutException;
+
+/**
+ * Ensure that any Contextualisations that pass through are serialised according to the strategy imposed by our Collapser.
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.9 $
+ */
+public class SerialContextualiser extends AbstractDelegatingContextualiser {
+
+	protected final Collapser _collapser;
+	protected final Sync _dummyLock=new NullSync();
+	protected final Map _map;
+	protected final Log _lockLog=LogFactory.getLog("org.codehaus.wadi.LOCKS");
+
+	public SerialContextualiser(Contextualiser next, Collapser collapser, Map map) {
+		super(next);
+		_collapser=collapser;
+		_map=map;
+	}
+
+	public boolean contextualise(InvocationContext invocationContext, String id, Immoter immoter, Sync invocationLock, boolean exclusiveOnly) throws InvocationException {
+		if (invocationLock!=null) {
+			// someone is already doing a promotion from further up the
+			// stack - do nothing other than delegate...
+			return _next.contextualise(invocationContext, id, immoter, invocationLock, exclusiveOnly);
+		} else {
+			// the promotion begins here...
+			// allocate a lock and continue...
+			invocationLock=_collapser.getLock(id);
+			boolean invocationLockAcquired=false;
+			try {
+				Utils.acquireUninterrupted("Invocation", id, invocationLock);
+				invocationLockAcquired=true;
+			} catch (TimeoutException e) {
+				_log.error("unexpected timeout - proceding without lock", e);
+			}
+
+			try {
+				// whilst we were waiting for the motionLock, the session in question may have been moved back into memory somehow.
+				// before we proceed, confirm that this has not happened.
+				Context context=(Context)_map.get(id);
+				boolean found=false;
+				if (null!=context) {
+					// oops - it HAS happened...
+					if (_log.isWarnEnabled()) _log.warn("session has reappeared in memory whilst we were waiting to immote it...: "+id+ " ["+Thread.currentThread().getName()+"]"); // TODO - downgrade..
+					// overlap two locking systems until we have secured the session in memory, then run the request
+					// and release the lock.
+					// TODO - we really need to take a read lock before we release the motionLock...
+					found=immoter.contextualise(invocationContext, id, context, invocationLock);
+					// although we did find the context it may have left this contextualiser before we finally acquire its lock.
+					// be prepared to continue dowm the stack looking for it.
+				}
+
+				if (!found) {
+					// session was not promoted whilst we were waiting for motionLock. Continue down Contextualiser stack
+					// it may be below us...
+					// lock is to be released as soon as context is available to subsequent contextualisations...
+					found=_next.contextualise(invocationContext, id, immoter, invocationLock, exclusiveOnly);
+				}
+				invocationLockAcquired=!found;
+				return found;
+			} finally {
+				if (invocationLockAcquired) {
+					Utils.release("Invocation", id, invocationLock);
+				}
+			}
+		}
+	}
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SessionToContextPoolAdapter.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SessionToContextPoolAdapter.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SessionToContextPoolAdapter.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SessionToContextPoolAdapter.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,54 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import org.codehaus.wadi.Context;
+import org.codehaus.wadi.ContextPool;
+import org.codehaus.wadi.Session;
+import org.codehaus.wadi.SessionConfig;
+import org.codehaus.wadi.SessionPool;
+
+
+/**
+ * Hack - plasters over a difference in the API between the Manager and Contextualiser
+ * stacks. Sessions appear in both stacks, and must be pooled by the same object, but with
+ * different APIs. To be resolved ASAP.
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.1 $
+ */
+public class SessionToContextPoolAdapter implements ContextPool {
+
+    protected final SessionPool _pool;
+    
+    public SessionToContextPoolAdapter(SessionPool pool) {
+        super();
+        _pool=pool;
+    }
+    
+    public void init(SessionConfig config) {
+        _pool.init(config);
+    }
+
+    public void put(Context context) {
+        _pool.put((Session)context);
+    }
+
+    public Context take() {
+        return _pool.take();
+    }
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SessionWrapper.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SessionWrapper.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SessionWrapper.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SessionWrapper.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,80 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import java.util.Collections;
+import java.util.Enumeration;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionContext;
+
+import org.codehaus.wadi.Session;
+
+/**
+ * Wraps a Session instance, presenting ONLY an HttpSession facade to the application.
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.1 $
+ */
+
+public class SessionWrapper implements HttpSession {
+
+    protected final Session _session;
+
+    public SessionWrapper(Session session) {
+        _session=session;
+    }
+
+    // delegate to Session
+    public long getCreationTime() {return _session.getCreationTime();}
+    public long getLastAccessedTime() {return _session.getLastAccessedTime();} 
+    public void setMaxInactiveInterval(int interval) {_session.setMaxInactiveInterval(interval);}
+    public int getMaxInactiveInterval() {return _session.getMaxInactiveInterval();}
+    public boolean isNew() {return _session.isNew();}
+    public String getId() {return _session.getId();}
+
+    public void setAttribute(String name, Object value) {
+        if (null==value)
+            _session.removeAttribute(name);
+        else
+            _session.setAttribute(name, value);
+    }
+    
+    public Object getAttribute(String name) {return _session.getAttribute(name);}
+    public void removeAttribute(String name) {_session.removeAttribute(name);}
+    public Enumeration getAttributeNames() {return _session.getAttributeNameEnumeration();}
+    public String[] getValueNames() {return _session.getAttributeNameStringArray();}
+    
+    // delegate to Wrapper
+    public Object getValue(String name) {return getAttribute(name);}
+    public void putValue(String name, Object value) {setAttribute(name, value);}
+    public void removeValue(String name) {removeAttribute(name);}
+
+    // delegate to Manager
+    public ServletContext getServletContext() {return _session.getConfig().getServletContext();}
+    public void invalidate() {_session.getConfig().destroy(_session);}
+    
+    // handle ourselves...
+    protected static final HttpSessionContext _httpSessionContext=new HttpSessionContext() {
+        protected final Enumeration _emptyEnumeration=Collections.enumeration(Collections.EMPTY_LIST);
+        public HttpSession getSession(String sessionId) {return null;}
+        public Enumeration getIds() {return _emptyEnumeration;}
+    };
+    public HttpSessionContext getSessionContext(){return _httpSessionContext;}
+
+}

Added: incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SharedStoreContextualiser.java
URL: http://svn.apache.org/viewcvs/incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SharedStoreContextualiser.java?rev=356933&view=auto
==============================================================================
--- incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SharedStoreContextualiser.java (added)
+++ incubator/wadi/trunk/modules/core/src/main/java/org/codehaus/wadi/impl/SharedStoreContextualiser.java Wed Dec 14 15:32:56 2005
@@ -0,0 +1,134 @@
+/**
+ *
+ * Copyright 2003-2005 Core Developers Network Ltd.
+ *
+ *  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.codehaus.wadi.impl;
+
+import org.codehaus.wadi.Collapser;
+import org.codehaus.wadi.Contextualiser;
+import org.codehaus.wadi.ContextualiserConfig;
+import org.codehaus.wadi.DistributableContextualiserConfig;
+import org.codehaus.wadi.Emoter;
+import org.codehaus.wadi.Immoter;
+import org.codehaus.wadi.InvocationContext;
+import org.codehaus.wadi.InvocationException;
+import org.codehaus.wadi.Motable;
+import org.codehaus.wadi.Store;
+import org.codehaus.wadi.StoreMotable;
+
+import EDU.oswego.cs.dl.util.concurrent.Sync;
+
+/**
+ * A Contextualiser which stores its Contexts in a shared database via JDBC.
+ * On shutdown of the cluster's last node, all extant sessions will be demoted to here.
+ * On startup of the cluster's first node, all sessions stored here will be promoted upwards.
+ *
+ * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+ * @version $Revision: 1.6 $
+ */
+
+public class SharedStoreContextualiser extends AbstractSharedContextualiser {
+	
+	protected final DatabaseStore _store;
+	protected final Immoter _immoter;
+	protected final Emoter _emoter;
+	
+	public SharedStoreContextualiser(Contextualiser next, Collapser collapser, boolean clean, DatabaseStore store) {
+		super(next, new CollapsingLocker(collapser), clean);
+		_store=store;
+		_immoter=new SharedJDBCImmoter();
+		_emoter=new SharedJDBCEmoter();
+	}
+	
+	public String getStartInfo() {
+		return "["+_store.getLabel()+"/"+_store.getTable()+"]";
+	}
+	
+	
+	public void init(ContextualiserConfig config) {
+		super.init(config);
+		if (_clean)
+			_store.clean();
+	}
+	
+	public Immoter getImmoter(){return _immoter;}
+	public Emoter getEmoter(){return _emoter;}
+	
+	public Immoter getDemoter(String name, Motable motable) {
+		// TODO - should check _next... - just remove when we have an evicter sorted
+		return new SharedJDBCImmoter();
+	}
+	
+	public Motable get(String id) {
+		throw new UnsupportedOperationException();
+	}
+	
+	/**
+	 * An Emoter that deals in terms of SharedJDBCMotables
+	 *
+	 * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
+	 * @version $Revision: 1.6 $
+	 */
+	public class SharedJDBCImmoter extends AbstractImmoter {
+		
+		public Motable nextMotable(String name, Motable emotable) {
+			StoreMotable motable=_store.create();
+			motable.init(_store);
+			return motable; // TODO - Pool, maybe as ThreadLocal
+		}
+		
+		public String getInfo() {
+			return _store.getDescription();
+		}
+	}
+	
+	public class SharedJDBCEmoter extends AbstractChainedEmoter {
+		
+		public String getInfo() {
+			return _store.getDescription();
+		}
+	}
+	
+	class SharedPutter implements Store.Putter {
+		
+		protected final Emoter _emoter;
+		protected final Immoter _immoter;
+		
+		public SharedPutter(Emoter emoter, Immoter immoter) {
+			_emoter=emoter;
+			_immoter=immoter;
+		}
+		
+		public void put(String name, Motable motable) {
+			Utils.mote(_emoter, _immoter, motable, name);
+		}
+	}
+	
+	public void load(Emoter emoter, Immoter immoter) {
+		// this should only happen when we are the first node in the cluster...
+		_store.load(new SharedPutter(emoter, immoter), ((DistributableContextualiserConfig)_config).getAccessOnLoad());
+	}
+	
+	public Emoter getEvictionEmoter() {throw new UnsupportedOperationException();} // FIXME
+	public void expire(Motable motable) {throw new UnsupportedOperationException();} // FIXME
+	
+	/**
+	 * Shared Contextualisers do nothing at runtime. They exist only to load data at startup and store it at shutdown.
+	 */
+	public boolean contextualise(InvocationContext invocationContext, String id, Immoter immoter, Sync motionLock, boolean exclusiveOnly) throws InvocationException {
+		return false;
+	}
+	
+}