// ========================================================================
// Copyright 2008 Mort Bay Consulting Pty. 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.mortbay.jetty.openspaces;
import java.util.HashSet;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.AbstractSessionIdManager;
import org.mortbay.jetty.servlet.AbstractSessionManager;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.log.Log;
import org.openspaces.core.GigaSpace;
import org.openspaces.core.GigaSpaceConfigurer;
import org.openspaces.core.space.UrlSpaceConfigurer;
import org.openspaces.core.space.cache.LocalCacheSpaceConfigurer;
import com.gigaspaces.annotation.pojo.SpaceId;
import com.gigaspaces.annotation.pojo.SpaceRouting;
/**
* GigaspacesSessionIdManager
*
* A Jetty SessionIDManager where the in-use session ids are stored
* in a data grid "cloud".
*/
public class GigaSessionIdManager extends AbstractSessionIdManager
{
protected HashSet<Id> _sessionIds = new HashSet<Id>();
protected long _scavengeIntervalSec = 60 * 10; //10mins - TODO could move back to the SessionManager?
protected String _spaceUrl;
protected GigaSpace _space;
protected long _waitMsec = 5000L; //time in msec to wait on read operations
/**
* Id
*
* Class to hold the session id. This is really only needed
* for gigaspaces so that we can annotate routing information etc.
*/
public static class Id
{
private String _id;
public Id()
{}
public Id(String id)
{
_id=id;
}
public void setId(String id)
{
_id=id;
}
@SpaceId
@SpaceRouting
public String getId()
{
return _id;
}
public boolean equals(Object o)
{
if (o == null)
return false;
Id targetId = (Id)o;
if (targetId.getId() == _id)
return true;
if (targetId.getId().equals(_id))
return true;
return false;
}
}
public GigaSessionIdManager(Server server)
{
super(server);
}
public GigaSessionIdManager(Server server, Random random)
{
super(server, random);
}
public void setSpaceUrl (String url)
{
_spaceUrl=url;
}
public String getSpaceUrl ()
{
return _spaceUrl;
}
public void setWaitMs (long msec)
{
_waitMsec=msec;
}
public long getWaitMs ()
{
return _waitMsec;
}
public void addSession(HttpSession session)
{
if (session == null)
return;
synchronized (_sessionIds)
{
if (session instanceof GigaSessionManager.Session)
{
String id = ((GigaSessionManager.Session)session).getClusterId();
try
{
Id theId = new Id(id);
add(theId);
_sessionIds.add(theId);
if (Log.isDebugEnabled()) Log.debug("Added id "+id);
}
catch (Exception e)
{
Log.warn("Problem storing session id="+id, e);
}
}
else
throw new IllegalStateException ("Session is not a Gigaspaces session");
}
}
public String getClusterId(String nodeId)
{
int dot=nodeId.lastIndexOf('.');
return (dot>0)?nodeId.substring(0,dot):nodeId;
}
public String getNodeId(String clusterId, HttpServletRequest request)
{
if (_workerName!=null)
return clusterId+'.'+_workerName;
return clusterId;
}
public boolean idInUse(String id)
{
if (id == null)
return false;
String clusterId = getClusterId(id);
Id theId = new Id(clusterId);
synchronized (_sessionIds)
{
if (_sessionIds.contains(theId))
return true; //optimisation - if this session is one we've been managing, we can check locally
//otherwise, we need to go to the space to check
try
{
return exists(theId);
}
catch (Exception e)
{
Log.warn("Problem checking inUse for id="+clusterId, e);
return false;
}
}
}
public void invalidateAll(String id)
{
//take the id out of the list of known sessionids for this node
removeSession(id);
synchronized (_sessionIds)
{
//tell all contexts that may have a session object with this id to
//get rid of them
Handler[] contexts = _server.getChildHandlersByClass(WebAppContext.class);
for (int i=0; contexts!=null && i<contexts.length; i++)
{
AbstractSessionManager manager = ((AbstractSessionManager)((WebAppContext)contexts[i]).getSessionHandler().getSessionManager());
if (manager instanceof GigaSessionManager)
{
((GigaSessionManager)manager).invalidateSession(id);
}
}
}
}
public void removeSession(HttpSession session)
{
if (session == null)
return;
removeSession(((GigaSessionManager.Session)session).getClusterId());
}
public void removeSession (String id)
{
if (id == null)
return;
synchronized (_sessionIds)
{
if (Log.isDebugEnabled())
Log.debug("Removing session id="+id);
try
{
Id theId = new Id(id);
_sessionIds.remove(theId);
delete(theId);
}
catch (Exception e)
{
Log.warn("Problem removing session id="+id, e);
}
}
}
public void setSpace (GigaSpace space)
{
_space=space;
}
public GigaSpace getSpace ()
{
return _space;
}
/**
* Start up the id manager.
*
* @see org.mortbay.jetty.servlet.AbstractSessionIdManager#doStart()
*/
public void doStart()
{
try
{
if (_space==null)
initSpace();
super.doStart();
}
catch (Exception e)
{
Log.warn("Problem initialising session ids", e);
throw new IllegalStateException (e);
}
}
/**
* Stop the scavenger.
*
* @see org.mortbay.component.AbstractLifeCycle#doStop()
*/
public void doStop ()
throws Exception
{
super.doStop();
}
protected void initSpace ()
throws Exception
{
if (_spaceUrl==null)
throw new IllegalStateException ("No url for space");
UrlSpaceConfigurer usc = new UrlSpaceConfigurer(_spaceUrl);
LocalCacheSpaceConfigurer lcsc = new LocalCacheSpaceConfigurer(usc.space());
GigaSpaceConfigurer gigaSpaceConfigurer = new GigaSpaceConfigurer(usc.space());
_space = gigaSpaceConfigurer.gigaSpace();
}
protected void add (Id id)
throws Exception
{
_space.write(id);
}
protected void delete (Id id)
throws Exception
{
_space.takeIfExists(id, getWaitMs());
if (Log.isDebugEnabled()) Log.debug ("Deleted id from space: id="+id);
}
protected boolean exists (Id id)
throws Exception
{
Id idFromSpace = (Id)_space.readIfExists(id, getWaitMs());
if (Log.isDebugEnabled()) Log.debug("Id="+id+(idFromSpace==null?"does not exist":"exists"));
if (idFromSpace==null)
return false;
return true;
}
}