All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.directwebremoting.impl.DefaultScriptSession Maven / Gradle / Ivy

package org.directwebremoting.impl;

import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.event.ScriptSessionBindingEvent;
import org.directwebremoting.event.ScriptSessionBindingListener;
import org.directwebremoting.extend.ConverterManager;
import org.directwebremoting.extend.RealScriptSession;
import org.directwebremoting.extend.ScriptBufferUtil;
import org.directwebremoting.extend.ScriptSessionManager;
import org.directwebremoting.extend.Sleeper;

/**
 * An implementation of ScriptSession and RealScriptSession.
 * 

You should note that {@link DefaultScriptSession} and * {@link DefaultScriptSessionManager} make calls to each other and you should * take care not to break any constraints in inheriting from these classes. * @author Joe Walker [joe at getahead dot ltd dot uk] */ public class DefaultScriptSession implements RealScriptSession { /** * Simple constructor * @param id The new unique identifier for this session * @param page The URL of the page on which we sit * @param manager The manager that created us * @param converterManager ... * @param jsonOutput ... */ protected DefaultScriptSession(String id, ScriptSessionManager manager, String page, ConverterManager converterManager, boolean jsonOutput) { this.id = id; if (id == null) { throw new IllegalArgumentException("id can not be null"); } this.manager = manager; this.page = page; this.converterManager = converterManager; this.jsonOutput = jsonOutput; this.creationTime = System.currentTimeMillis(); this.lastAccessedTime = creationTime; } public Object getAttribute(String name) { invalidateIfNeeded(); return attributes.get(name); } public void setAttribute(String name, Object value) { invalidateIfNeeded(); if (value != null) { if (value instanceof ScriptSessionBindingListener) { ScriptSessionBindingListener listener = (ScriptSessionBindingListener) value; listener.valueBound(new ScriptSessionBindingEvent(this, name)); } attributes.put(name, value); } else { removeAttribute(name); } } public void removeAttribute(String name) { invalidateIfNeeded(); Object value = attributes.remove(name); if (value != null && value instanceof ScriptSessionBindingListener) { ScriptSessionBindingListener listener = (ScriptSessionBindingListener) value; listener.valueUnbound(new ScriptSessionBindingEvent(this, name)); } } public Iterator getAttributeNames() { invalidateIfNeeded(); Set keys = Collections.unmodifiableSet(new HashSet(attributes.keySet())); return keys.iterator(); } public void invalidate() { invalidated = true; // attributes is a concurrent map and can be safely iterated. for (Map.Entry entry : attributes.entrySet()) { Object value = entry.getValue(); if (value instanceof ScriptSessionBindingListener) { ScriptSessionBindingListener listener = (ScriptSessionBindingListener) value; listener.valueUnbound(new ScriptSessionBindingEvent(this, entry.getKey())); } } if (manager instanceof DefaultScriptSessionManager) { ((DefaultScriptSessionManager) manager).invalidate(this); } } public boolean isInvalidated() { return invalidated; } public String getId() { return id; } public long getCreationTime() { invalidateIfNeeded(); return creationTime; } public long getLastAccessedTime() { // For many accesses here we check to see if we should invalidate // ourselves, but getLastAccessedTime() is used as part of the process // that DefaultScriptSessionManager goes through in order to check // everything for validity. So if we do this check here then DSSM will // give a ConcurrentModificationException if anything does timeout // checkNotInvalidated(); return lastAccessedTime; } public void addScript(ScriptBuffer script) { invalidateIfNeeded(); if (script == null) { throw new NullPointerException("null script"); } synchronized (this.scripts) { scripts.add(ScriptBufferUtil.createOutput(script, converterManager, jsonOutput)); } synchronized (sleeperLock) { if (sleeper != null) { sleeper.wakeUpForData(); } } } public void addRunnable(final Runnable runnable) { invalidateIfNeeded(); if (runnable == null) { throw new NullPointerException("null runnable"); } synchronized (this.scripts) { scripts.add(runnable); } synchronized (sleeperLock) { if (sleeper != null) { sleeper.wakeUpForData(); } } } public void setSleeper(Sleeper sleeper) { invalidateIfNeeded(); synchronized (sleeperLock) { this.sleeper = sleeper; } } @SuppressWarnings("hiding") public void clearSleeper(Sleeper sleeper) { invalidateIfNeeded(); synchronized (sleeperLock) { if (this.sleeper == sleeper) { this.sleeper = null; } } } public Script getScript(long scriptIndex) { synchronized (scripts) { if (scriptsOffset < 0) { throw new IllegalStateException("Confirmed script index must be set before accessing scripts."); } int listIndex = (int) (scriptIndex - scriptsOffset); if (listIndex < 0) { listIndex = 0; } if (listIndex < scripts.size()) { final Object script = scripts.get(listIndex); final long resultingScriptIndex = scriptsOffset + listIndex; return new Script() { public long getIndex() { return resultingScriptIndex; } public Object getScript() { return script; } }; } else { return null; } } } /* (non-Javadoc) * @see org.directwebremoting.extend.RealScriptSession#confirmTransfer(long) */ public void confirmScripts(long confirmedScriptIndex) { long nextScriptIndex = confirmedScriptIndex + 1; synchronized (scripts) { // If this is a re-born script session with uninitialized script offset, or // client's offset is outside our range for other reasons, then just // fast-forward ourselves to the client's offset so it will continue // receiving an unbroken series of scripts if (scriptsOffset < 0 || nextScriptIndex > scriptsOffset + scripts.size()) { scriptsOffset = nextScriptIndex; } // Purge confirmed long remove = nextScriptIndex - scriptsOffset; // if we are on offset 10 and 10 is confirmed, we should remove 1 if (remove > 0) { for(long i=0; i manager.getScriptSessionTimeout()) { synchronized (sleeperLock) { if (connectionValidationTime > 0 && now < connectionValidationTime) { // We are waiting for an already started connection validation so do nothing return; } else if (connectionValidationTime == 0 && sleeper != null) { // Start a connection validation by closing the current poll int disconnectedTime = sleeper.wakeUpToClose(); connectionValidationTime = now + disconnectedTime + connectionValidationTimeout; return; } } // No ongoing connection validation and no connection (sleeper) so this is // the end of the road invalidate(); } } /** * Set the time to wait for client to poll when asking for connection validation. * @param connectionValidationTimeout .. */ public void setConnectionValidationTimeout(int connectionValidationTimeout) { this.connectionValidationTimeout = connectionValidationTimeout; } @Override public int hashCode() { return 572 + id.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (!this.getClass().equals(obj.getClass())) { return false; } DefaultScriptSession that = (DefaultScriptSession) obj; return this.id.equals(that.id); } @Override public String toString() { return "DefaultScriptSession[id=" + getDebugName() + "]"; } /** * A very short name for debugging purposes * @return ... */ protected String getDebugName() { int slashPos = id.indexOf('/'); return id.substring(0, 4) + (slashPos >= 0 ? id.substring(slashPos, slashPos+5) : ""); } /** * How we convert parameters */ protected ConverterManager converterManager = null; /** * Are we outputting in JSON mode? */ protected boolean jsonOutput = false; /** * The server side attributes for this page. *

GuardedBy("attributes") */ protected final Map attributes = new ConcurrentHashMap(); /** * When the the web page that we represent last contact us using DWR? */ private volatile long lastAccessedTime = 0L; /** * When > 0 we are waiting for the client until the set time to validate the connection by polling again */ private volatile long connectionValidationTime = 0; /** * How long to wait for a client to poll when asking for connection validation */ private int connectionValidationTimeout = 10000; /** * Have we been made invalid? */ private volatile boolean invalidated = false; /** * The sleeper that waits for new data. */ protected Sleeper sleeper = null; /** * The lock for synchronizing sleepers access. */ protected Object sleeperLock = new Object(); /** * The script index of the first item in the scripts collection. */ protected long scriptsOffset = 0; /** * The list of waiting scripts (String or Runnable). *

GuardedBy("self") for iteration and compound actions. */ protected final LinkedList scripts = new LinkedList(); /** * What is our page session id? *

This should not need careful synchronization since it is unchanging */ protected final String id; /** * When we we created? *

This should not need careful synchronization since it is unchanging */ protected final long creationTime; /** * The page being viewed. */ protected String page; /** * The corresponding HttpSession id (if any) */ protected String httpSessionId; /** * The session manager that collects sessions together *

This should not need careful synchronization since it is unchanging */ protected final ScriptSessionManager manager; }