/*******************************************************************************
* Copyright (c) 2002-2006 Innoopract Informationssysteme GmbH.
* 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:
* Innoopract Informationssysteme GmbH - initial API and implementation
******************************************************************************/
package com.w4t.engine.util;
import java.lang.ref.WeakReference;
import java.util.*;
import javax.servlet.http.*;
import org.eclipse.rwt.internal.*;
import com.w4t.WebComponentStatistics;
import com.w4t.engine.W4TModel;
/** <p>encapsulates a list of W4TModel objects. List entries are stored
* with weak references, so the references to the list members that are
* kept here do not prevent the garbage collector to clean them away.</p>
*
* <p>Implementation as singleton.</p>
*/
public final class W4TModelList {
private static int unboundCount = 0;
private static boolean unboundThreadRunning = false;
private static W4TModelList _instance;
/** the internal data structure for the list. */
private final Hashtable list;
/** Creates the singleton instance of W4TModelList */
private W4TModelList() {
list = new Hashtable();
}
public synchronized static W4TModelList getInstance() {
if( _instance == null ) {
_instance = new W4TModelList();
}
return _instance;
}
// list handling
////////////////
/** adds the specified W4TModel to the list, using the specified id.
* We use the session id for this purpose. */
public void add( final HttpSession session, final W4TModel model ) {
// put model under its respective sessionId into list
WeakReference modelWeakRef = new WeakReference( model );
list.put( session.getId(), modelWeakRef );
// register a listener to remove model if its session is invalidated
HttpSessionBindingListener listener = new HttpSessionBindingListener() {
private String sessionId;
public void valueBound( final HttpSessionBindingEvent event ) {
sessionId = event.getSession().getId();
}
public void valueUnbound( final HttpSessionBindingEvent event ) {
W4TModelList.getInstance().remove( sessionId );
W4TModelList.getInstance().forceGC();
}
};
session.setAttribute( "HttpSessionBindingListener", listener );
}
public W4TModel get( final String id ) {
WeakReference modelWeakRef = ( WeakReference )list.get( id );
W4TModel result = null;
if( modelWeakRef != null ) {
result = ( W4TModel )modelWeakRef.get();
}
return result;
}
/** removes the W4TModel that is stored with the specified id from the
* list. We use the session id for this purpose. */
public void remove( final String id ) {
WeakReference modelWeakRef = ( WeakReference )list.remove( id );
Object obj = modelWeakRef.get();
if( obj != null ) {
// TODO: Think about, if this is still necessary
// W4TModel model = ( W4TModel )obj;
// DelegateClassLoader dcl = model.getClassLoader();
// dcl.cleanup();
}
}
/** returns a WebComponentStatistics with information about the components,
* uptime etc. of all models in the list. */
public WebComponentStatistics getStatistics() {
WebComponentStatistics result = null;
Enumeration en = list.elements();
while( en.hasMoreElements() ) {
WeakReference modelWeakRef = ( WeakReference )en.nextElement();
Object obj = modelWeakRef.get();
if( obj != null ) {
W4TModel model = ( W4TModel )obj;
WebComponentStatistics statistics = model.getStatistics();
if( statistics != null ) {
result = addUpStatistics( result, statistics );
}
}
}
if( result != null ) {
result.setSessionCount( getListSize() );
}
return result;
}
/** returns an array with the currently added W4TModel instances to this
* W4TModelList. */
public W4TModel[] getList() {
List buffer = new Vector();
synchronized ( list ) {
Enumeration en = list.elements();
while( en.hasMoreElements() ) {
WeakReference modelWeakRef = ( WeakReference )en.nextElement();
Object obj = modelWeakRef.get();
if( obj != null ) {
buffer.add( obj );
}
}
}
W4TModel[] result = new W4TModel[ buffer.size() ];
buffer.toArray( result );
return result;
}
public void cleanup() {
W4TModel[] list = getList();
for( int i = 0; i < list.length; i++ ) {
list[ i ].cleanup();
}
}
private void forceGC() {
// determine the maximum amount of sessions which could be released
// until the garbage collection will be forced
IConfiguration configuration = ConfigurationReader.getConfiguration();
IInitialization initialization = configuration.getInitialization();
long maxSessionUnboundToForceGC
= initialization.getMaxSessionUnboundToForceGC();
if( maxSessionUnboundToForceGC > 0 ) {
if( !unboundThreadRunning ) {
unboundCount++;
}
if( getUnboundCount() >= maxSessionUnboundToForceGC ) {
unboundThreadRunning = true;
unboundCount = 0;
Thread forceGC = new Thread() {
public void run() {
System.out.println( "\n***************************\nforceGC - step 1\n" );
System.gc();
synchronized( this ) {
try {
this.wait( 100 );
} catch( Exception shouldNotHappen ) {
System.out.println( shouldNotHappen );
}
}
System.out.println( "\n***************************\nforceGC - step 2\n" );
System.gc();
unboundThreadRunning = false;
}
};
forceGC.start();
}
}
}
synchronized void setUnboundCount( final int unboundCount ) {
W4TModelList.unboundCount = unboundCount;
}
int getUnboundCount() {
return unboundCount;
}
// helping methods
//////////////////
/** adds all statistics found in statsToAdd to the corresponding statistics
* elements in stats (which contains then the sum of both passed
* WebComponentStatistics objects). */
private WebComponentStatistics addUpStatistics(
final WebComponentStatistics stats,
final WebComponentStatistics statsToAdd )
{
WebComponentStatistics result = stats;
if( stats == null ) {
result = statsToAdd;
} else {
stats.integrate( statsToAdd );
}
return result;
}
private int getListSize() {
return list.values().size();
}
}