org.eclipse.birt.report.session.ViewingSessionManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of viewservlets Show documentation
Show all versions of viewservlets Show documentation
A component of the BIRT runtime
/*************************************************************************************
* Copyright (c) 2008 Actuate Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Actuate Corporation - Initial implementation.
************************************************************************************/
package org.eclipse.birt.report.session;
import java.io.Serializable;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import org.eclipse.birt.report.exception.ViewerException;
import org.eclipse.birt.report.resource.BirtResources;
import org.eclipse.birt.report.resource.ResourceConstants;
/**
* BIRT viewing session manager.
This class manages the BIRT viewing
* sessions that reside under a master HTTP session. Each HTTP session can have
* more than one BIRT viewing session. This class has two ways to trigger the
* cleanup:
*
* - After a given timeout value is reached. This value is checked after each
* call to createSession().
* - After a given BIRT viewing session count threshold has been reached.
* After each cleanup, the session count threshold will be increased using the
* following formula:
sessionCountThreshold = remainingSessionsCount +
* remainingSessionsCount * sessionCountThreshold
. The
* remainingSessionsCount is the number of sessions that remain after cleanup.
* If the result of this calculation is smaller than
* minimumSessionCountThreshold, then sessionCountThreshold will take the value
* of minimumSessionCountThreshold.
*
*/
public class ViewingSessionManager implements IViewingSessionManager,
HttpSessionBindingListener, Serializable
{
private static final long serialVersionUID = -7623325281275814412L;
private ViewingCache viewingCache;
private long nextCleanupTime;
/**
* Linked hash map containing the ViewingSession objects in access order.
*/
private Map sessions;
/**
* HTTP session ID to which the contained BIRT viewing sessions belong.
*/
private String httpSessionId;
/**
* Expired flag.
*/
private boolean expired;
/**
* Session count threshold after which the cleanup process will be
* triggered. This value will change dynamically according to the number of
* remaining sessions after cleanup.
*/
private int sessionCountThreshold;
private ViewingSessionConfig config;
/**
* Wrapper for the IViewingSession interface, to hook the interface's
* methods.
*/
private static class ViewingSessionWrapper implements IViewingSession, Serializable
{
private static final long serialVersionUID = -5837896305154946951L;
private IViewingSession session;
private ViewingSessionManager manager;
public ViewingSessionWrapper( ViewingSessionManager manager,
IViewingSession session )
{
this.manager = manager;
this.session = session;
}
public IViewingSession getWrappedSession( )
{
return session;
}
public String getCachedReportDocument( String reportFile,
String viewerId )
{
return session.getCachedReportDocument( reportFile, viewerId );
}
public String getId( )
{
return session.getId( );
}
public String getImageTempFolder( )
{
return session.getImageTempFolder( );
}
public Date getLastAccess( )
{
return session.getLastAccess( );
}
public void invalidate( )
{
synchronized ( manager )
{
session.invalidate( );
// remove the session from the map
manager.sessions.remove( session.getId( ) );
}
}
public boolean isExpired( )
{
return session.isExpired( );
}
public boolean isLocked( )
{
return session.isLocked( );
}
public void lock( )
{
session.lock( );
}
public void unlock( )
{
session.unlock( );
manager.refreshSession( session );
manager.cleanUp( );
}
public void refresh( )
{
manager.refreshSession( session );
}
}
/**
* Instantiates a new viewing session manager.
*
* @param viewingCache
* viewing cache instance
* @param httpSessionId
* HTTP session ID
* @param config
* viewing session configuration
*/
public ViewingSessionManager( ViewingCache viewingCache,
String httpSessionId, ViewingSessionConfig config )
{
this.httpSessionId = httpSessionId;
this.viewingCache = viewingCache;
this.config = config;
this.sessionCountThreshold = config.getMinimumSessionCountThreshold( );
// using LinkedHashMap to keep the insertion order and access time (LRU)
this.sessions = new LinkedHashMap( config
.getMinimumSessionCountThreshold( ), config
.getSessionCountThresholdFactor( ), true );
this.expired = false;
this.nextCleanupTime = new Date( ).getTime( )
+ config.getSessionTimeout( ) * 1000l;
}
/**
* Returns the cache manager used by this manager.
*
* @return the cacheManager
*/
public ViewingCache getCacheManager( )
{
return viewingCache;
}
/**
* @see
* org.eclipse.birt.report.session.IViewingSessionManager#getHttpSessionId()
*/
public String getHttpSessionId( )
{
return httpSessionId;
}
/**
* @see
* org.eclipse.birt.report.session.IViewingSessionManager#createSession()
*/
public synchronized IViewingSession createSession( ) throws ViewerException
{
checkExpired( );
cleanUp( );
if ( config.getMaximumSessionCount( ) > 0
&& sessions.size( ) >= config.getMaximumSessionCount( ) )
{
switch ( config.getMaxSessionCountPolicy( ) )
{
case SESSION_POLICY_DISCARD_NEW :
throw new ViewerException(
BirtResources
.getMessage( ResourceConstants.GENERAL_ERROR_VIEWING_SESSION_MAX_REACHED ) );
case SESSION_POLICY_DISCARD_OLDEST :
if ( !deleteOldestSession( ) )
{
// no oldest session could be removed (lock)
throw new ViewerException(
BirtResources
.getMessage( ResourceConstants.GENERAL_ERROR_VIEWING_SESSION_MAX_REACHED ) );
}
break;
}
}
IViewingSession newSession = new ViewingSessionWrapper( this,
new ViewingSession( httpSessionId, viewingCache ) );
sessions.put( newSession.getId( ), newSession );
return newSession;
}
/**
* @see
* org.eclipse.birt.report.session.IViewingSessionManager#getSession(java
* .lang.String)
*/
public synchronized IViewingSession getSession( String id )
{
checkExpired( );
ViewingSessionWrapper session = (ViewingSessionWrapper) sessions
.get( id );
if ( session != null )
{
session.getWrappedSession( ).refresh( );
}
return session;
}
/**
* @see org.eclipse.birt.report.session.IViewingSessionManager#invalidate()
*/
public synchronized void invalidate( )
{
if ( expired )
{
return;
}
expired = true;
try
{
for ( Iterator> i = sessions
.entrySet( ).iterator( ); i.hasNext( ); )
{
Map.Entry entry = i.next( );
IViewingSession session = ((ViewingSessionWrapper) entry
.getValue( )).getWrappedSession( );
if ( !session.isExpired( ) )
{
while ( session.isLocked() )
{
session.unlock( );
}
session.invalidate( );
}
}
}
finally
{
// clear master session cache
viewingCache.clearSession( httpSessionId, null );
sessions.clear( );
}
}
/**
* Refreshes the given session by calling its refresh() method
* and by updating the internal map order.
* @param session viewing session
*/
private synchronized void refreshSession( IViewingSession session )
{
// touches the linked hash map to make it move
// the entry to the end
sessions.get( session.getId() );
session.refresh();
}
/**
* Removes a session id from the map.
*
* @param id
* session id
*/
public synchronized void removeSession( String id )
{
sessions.remove( id );
}
/**
* Requests a cleanup operation. The operation is only performed if the
* session count threshold or the timeout value has been reached.
*
* @param sessions
* sessions
*/
private synchronized void cleanUp( )
{
long now = new Date( ).getTime( );
if ( now >= nextCleanupTime || sessions.size( ) > sessionCountThreshold )
{
doCleanup( );
if ( sessions.size( ) > 0 )
{
// the oldest date is the first entry in the linked hash map
IViewingSession oldestSession = sessions.values( ).iterator( )
.next( );
nextCleanupTime = oldestSession.getLastAccess( ).getTime( )
+ config.getSessionTimeout( ) * 1000l;
}
else
{
nextCleanupTime = now + config.getSessionTimeout( ) * 1000l;
}
int minimumThreshold = config.getMinimumSessionCountThreshold( );
float factor = config.getSessionCountThresholdFactor( );
sessionCountThreshold = sessions.size( )
+ (int) ( sessions.size( ) * factor );
if ( sessionCountThreshold < minimumThreshold )
{
sessionCountThreshold = minimumThreshold;
}
}
}
/**
* Checks whether there are existing sessions that have expired and clean
* them up accordingly.
*
* @param sessions
* sessions
*/
private synchronized void doCleanup( )
{
if ( sessions.size( ) == 0 )
{
return;
}
long sessionTimeout = config.getSessionTimeout( ) * 1000l;
long currentTime = new java.util.Date( ).getTime( ) - sessionTimeout;
for ( Iterator> i = sessions
.entrySet( ).iterator( ); i.hasNext( ); )
{
Map.Entry entry = i.next( );
IViewingSession session = entry.getValue( );
Date lastAccess = session.getLastAccess( );
if ( currentTime >= lastAccess.getTime( ) && !session.isLocked( ) )
{
if ( !session.isExpired( ) )
{
( (ViewingSessionWrapper) session ).getWrappedSession( )
.invalidate( );
}
i.remove( );
}
else
{
// since the linked hash map is ordered by insertion, the
// iteration will stop as soon as the first non-expired session
// is met.
break;
}
}
}
/**
* Deletes the oldest session that is not locked regardless whether it is
* has expired or not.
*/
private boolean deleteOldestSession( )
{
for ( Iterator> i = sessions
.entrySet( ).iterator( ); i.hasNext( ); )
{
Map.Entry entry = i.next( );
IViewingSession session = entry.getValue( );
if ( !session.isLocked( ) )
{
( (ViewingSessionWrapper) session ).getWrappedSession( )
.invalidate( );
i.remove( );
return true;
}
}
return false;
}
/**
* Asserts that the session has not expired.
*
* @throws ViewingSessionExpiredException
*/
private void checkExpired( )
{
if ( expired )
{
throw new IllegalStateException(
BirtResources
.getMessage( ResourceConstants.GENERAL_ERROR_VIEWING_SESSION_EXPIRED ) );
}
}
/**
* @see javax.servlet.http.HttpSessionBindingListener#valueBound(javax.servlet.http.HttpSessionBindingEvent)
*/
public void valueBound( HttpSessionBindingEvent event )
{
}
/**
* @see javax.servlet.http.HttpSessionBindingListener#valueUnbound(javax.servlet.http.HttpSessionBindingEvent)
*/
public void valueUnbound( HttpSessionBindingEvent event )
{
synchronized (this)
{
if ( !expired )
{
invalidate();
}
}
}
}