Project: * Clean-up (Eclipse cruft, unused files, etc...) * Git-specific changes * Maven POMs clean-up and changes for the build system * Version set to 1.0.0-0 in the development branches * Maven plug-ins updated to latest versions * Very partial dev. documentation added
This commit is contained in:
parent
c74e30d5ba
commit
0665a760de
1439 changed files with 1020 additions and 1649 deletions
legacyworlds-server-beans-system/src
main
java/com/deepclone/lw/beans/sys
ConstantsAdministrationImpl.javaConstantsData.javaConstantsManagerBean.javaConstantsRegistrarBean.javaServerSessionData.javaSessionManagerBean.javaSystemStatusBean.javaTickerBean.javaTickerManagerBean.javaTickerTask.javaTickerTaskStatusHandler.javaTickerThread.java
resources/configuration
test
|
@ -0,0 +1,106 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.deepclone.lw.interfaces.sys.ConstantDefinition;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantsAdministration;
|
||||
import com.deepclone.lw.interfaces.sys.InvalidConstantValue;
|
||||
import com.deepclone.lw.interfaces.sys.UnknownConstantError;
|
||||
import com.deepclone.lw.sqld.sys.Constant;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The constants administration implementation mostly serves as a session-specific proxy for the
|
||||
* share {@link ConstantsData} instance.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
class ConstantsAdministrationImpl
|
||||
implements ConstantsAdministration
|
||||
{
|
||||
/** Shared data storage instance */
|
||||
private ConstantsData data;
|
||||
|
||||
private int admin;
|
||||
|
||||
|
||||
/**
|
||||
* Copies the required references.
|
||||
*
|
||||
* @param data
|
||||
* shared data storage instance
|
||||
* @param logger
|
||||
* session-specific logger
|
||||
*/
|
||||
ConstantsAdministrationImpl( ConstantsData data , int admin )
|
||||
{
|
||||
this.data = data;
|
||||
this.admin = admin;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ConstantsAdministration interface */
|
||||
@Override
|
||||
public Set< String > getCategories( )
|
||||
{
|
||||
return this.data.getCategories( );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ConstantsAdministration interface */
|
||||
@Override
|
||||
public Collection< ConstantDefinition > getConstants( String category )
|
||||
{
|
||||
List< Constant > constants = this.data.getCategory( category );
|
||||
List< ConstantDefinition > defs = new LinkedList< ConstantDefinition >( );
|
||||
if ( constants == null ) {
|
||||
return defs;
|
||||
}
|
||||
|
||||
// Convert data to immutable records
|
||||
for ( Constant constant : constants ) {
|
||||
ConstantDefinition def;
|
||||
if ( constant.getMaxValue( ) == null && constant.getMinValue( ) == null ) {
|
||||
def = new ConstantDefinition( constant.getName( ) , category , constant.getDescription( ) , constant
|
||||
.getValue( ) );
|
||||
} else if ( constant.getMaxValue( ) == null || constant.getMinValue( ) == null ) {
|
||||
boolean isMin = ( constant.getMaxValue( ) == null );
|
||||
Double val = isMin ? constant.getMinValue( ) : constant.getMaxValue( );
|
||||
def = new ConstantDefinition( constant.getName( ) , category , constant.getDescription( ) , constant
|
||||
.getValue( ) , val , isMin );
|
||||
} else {
|
||||
if ( constant.getMaxValue( ) == null && constant.getMinValue( ) == null ) {
|
||||
def = new ConstantDefinition( constant.getName( ) , category , constant.getDescription( ) ,
|
||||
constant.getValue( ) );
|
||||
} else if ( constant.getMaxValue( ) == null ) {
|
||||
def = new ConstantDefinition( constant.getName( ) , category , constant.getDescription( ) ,
|
||||
constant.getValue( ) , constant.getMinValue( ) , true );
|
||||
} else if ( constant.getMinValue( ) == null ) {
|
||||
def = new ConstantDefinition( constant.getName( ) , category , constant.getDescription( ) ,
|
||||
constant.getValue( ) , constant.getMaxValue( ) , false );
|
||||
} else {
|
||||
def = new ConstantDefinition( constant.getName( ) , category , constant.getDescription( ) , constant
|
||||
.getValue( ) , constant.getMinValue( ) , constant.getMaxValue( ) );
|
||||
}
|
||||
}
|
||||
defs.add( def );
|
||||
}
|
||||
|
||||
return defs;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ConstantsAdministration interface */
|
||||
@Override
|
||||
public void setConstant( String name , Double value )
|
||||
throws UnknownConstantError , InvalidConstantValue
|
||||
{
|
||||
this.data.setValue( name , value , this.admin );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,630 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.SqlParameter;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.deepclone.lw.cmd.admin.logs.LogLevel;
|
||||
import com.deepclone.lw.interfaces.eventlog.Logger;
|
||||
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantDefinition;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantsUser;
|
||||
import com.deepclone.lw.interfaces.sys.InvalidConstantValue;
|
||||
import com.deepclone.lw.interfaces.sys.UnknownConstantError;
|
||||
import com.deepclone.lw.sqld.sys.Constant;
|
||||
import com.deepclone.lw.utils.StoredProc;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class defines the shared data structures used by both the constants manager bean and its
|
||||
* administrative session instances. It also encapsulates all of the code used by both.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
class ConstantsData
|
||||
{
|
||||
/** Database interface */
|
||||
private SimpleJdbcTemplate dTemplate;
|
||||
|
||||
/** Transaction manager interface */
|
||||
private TransactionTemplate tTemplate;
|
||||
|
||||
/** System logger instance for the ConstantsManager component */
|
||||
private SystemLogger sysLog;
|
||||
|
||||
/** All registered constants, by name */
|
||||
private Map< String , Constant > registeredConstants;
|
||||
|
||||
/** Constants users that have received their initial notification */
|
||||
private Set< ConstantsUser > notifiedUsers = new HashSet< ConstantsUser >( );
|
||||
|
||||
/** Constants needed by each user */
|
||||
private Map< ConstantsUser , Set< String >> userConstants = new HashMap< ConstantsUser , Set< String > >( );
|
||||
|
||||
/** Users listening to each constant */
|
||||
private Map< String , Set< ConstantsUser >> constantUsers = new HashMap< String , Set< ConstantsUser > >( );
|
||||
|
||||
/** Constants by category */
|
||||
private Map< String , List< Constant >> categories;
|
||||
|
||||
private SimpleJdbcCall uocConstantNoBounds;
|
||||
private SimpleJdbcCall uocConstantSingleBound;
|
||||
private SimpleJdbcCall uocConstantTwoBounds;
|
||||
private StoredProc fSetConstant;
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the various references, creates the system logger used by the instance, then
|
||||
* loads all existing constants.
|
||||
*
|
||||
* @param dataSource
|
||||
* database interface
|
||||
* @param tTemplate
|
||||
* transaction manager interface
|
||||
* @param logger
|
||||
* logger bean interface
|
||||
*/
|
||||
ConstantsData( DataSource dataSource , TransactionTemplate tTemplate , Logger logger )
|
||||
{
|
||||
this.dTemplate = new SimpleJdbcTemplate( dataSource );
|
||||
|
||||
this.uocConstantNoBounds = new SimpleJdbcCall( dataSource );
|
||||
this.uocConstantNoBounds.withCatalogName( "sys" ).withFunctionName( "uoc_constant" );
|
||||
this.uocConstantNoBounds.withoutProcedureColumnMetaDataAccess( );
|
||||
this.uocConstantNoBounds.addDeclaredParameter( new SqlParameter( "cname" , Types.VARCHAR ) );
|
||||
this.uocConstantNoBounds.addDeclaredParameter( new SqlParameter( "cdesc" , Types.VARCHAR ) );
|
||||
this.uocConstantNoBounds.addDeclaredParameter( new SqlParameter( "catname" , Types.VARCHAR ) );
|
||||
this.uocConstantNoBounds.addDeclaredParameter( new SqlParameter( "value" , Types.REAL ) );
|
||||
|
||||
this.uocConstantSingleBound = new SimpleJdbcCall( dataSource );
|
||||
this.uocConstantSingleBound.withCatalogName( "sys" ).withFunctionName( "uoc_constant" );
|
||||
this.uocConstantSingleBound.withoutProcedureColumnMetaDataAccess( );
|
||||
this.uocConstantSingleBound.addDeclaredParameter( new SqlParameter( "cname" , Types.VARCHAR ) );
|
||||
this.uocConstantSingleBound.addDeclaredParameter( new SqlParameter( "cdesc" , Types.VARCHAR ) );
|
||||
this.uocConstantSingleBound.addDeclaredParameter( new SqlParameter( "catname" , Types.VARCHAR ) );
|
||||
this.uocConstantSingleBound.addDeclaredParameter( new SqlParameter( "value" , Types.REAL ) );
|
||||
this.uocConstantSingleBound.addDeclaredParameter( new SqlParameter( "boundary" , Types.REAL ) );
|
||||
this.uocConstantSingleBound.addDeclaredParameter( new SqlParameter( "is_min" , Types.BOOLEAN ) );
|
||||
|
||||
this.uocConstantTwoBounds = new SimpleJdbcCall( dataSource );
|
||||
this.uocConstantTwoBounds.withCatalogName( "sys" ).withFunctionName( "uoc_constant" );
|
||||
this.uocConstantTwoBounds.withoutProcedureColumnMetaDataAccess( );
|
||||
this.uocConstantTwoBounds.addDeclaredParameter( new SqlParameter( "cname" , Types.VARCHAR ) );
|
||||
this.uocConstantTwoBounds.addDeclaredParameter( new SqlParameter( "cdesc" , Types.VARCHAR ) );
|
||||
this.uocConstantTwoBounds.addDeclaredParameter( new SqlParameter( "catname" , Types.VARCHAR ) );
|
||||
this.uocConstantTwoBounds.addDeclaredParameter( new SqlParameter( "value" , Types.REAL ) );
|
||||
this.uocConstantTwoBounds.addDeclaredParameter( new SqlParameter( "min" , Types.REAL ) );
|
||||
this.uocConstantTwoBounds.addDeclaredParameter( new SqlParameter( "max" , Types.REAL ) );
|
||||
|
||||
this.fSetConstant = new StoredProc( dataSource , "sys" , "set_constant" );
|
||||
this.fSetConstant.addParameter( "cname" , Types.VARCHAR );
|
||||
this.fSetConstant.addParameter( "value" , Types.REAL );
|
||||
this.fSetConstant.addParameter( "admin" , Types.INTEGER );
|
||||
|
||||
this.tTemplate = tTemplate;
|
||||
this.sysLog = logger.getSystemLogger( "ConstantsManager" );
|
||||
|
||||
this.tTemplate.execute( new TransactionCallbackWithoutResult( ) {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
loadConstants( );
|
||||
}
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
private void loadConstants( )
|
||||
{
|
||||
String sql = "SELECT category , name , description , value , min , max FROM sys.constants_view";
|
||||
RowMapper< Constant > mapper = new RowMapper< Constant >( ) {
|
||||
|
||||
@Override
|
||||
public Constant mapRow( ResultSet rs , int rowNum )
|
||||
throws SQLException
|
||||
{
|
||||
Constant c = new Constant( );
|
||||
c.setCategory( rs.getString( "category" ) );
|
||||
c.setName( rs.getString( "name" ) );
|
||||
c.setDescription( rs.getString( "description" ) );
|
||||
c.setValue( rs.getDouble( "value" ) );
|
||||
c.setMinValue( (Float) rs.getObject( "min" ) );
|
||||
c.setMaxValue( (Float) rs.getObject( "max" ) );
|
||||
return c;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.registeredConstants = new HashMap< String , Constant >( );
|
||||
this.categories = new HashMap< String , List< Constant > >( );
|
||||
for ( Constant cnst : this.dTemplate.query( sql , mapper ) ) {
|
||||
this.registeredConstants.put( cnst.getName( ) , cnst );
|
||||
|
||||
List< Constant > category = this.categories.get( cnst.getCategory( ) );
|
||||
if ( category == null ) {
|
||||
category = new LinkedList< Constant >( );
|
||||
this.categories.put( cnst.getCategory( ) , category );
|
||||
}
|
||||
category.add( cnst );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updateConstants( List< ConstantDefinition > toUpdate )
|
||||
{
|
||||
for ( ConstantDefinition cDef : toUpdate ) {
|
||||
if ( cDef.maxValue == null && cDef.minValue == null ) {
|
||||
this.uocConstantNoBounds.execute( cDef.name , cDef.description , cDef.category , cDef.defaultValue );
|
||||
} else if ( cDef.maxValue == null ) {
|
||||
this.uocConstantSingleBound.execute( cDef.name , cDef.description , cDef.category , cDef.defaultValue ,
|
||||
cDef.minValue , true );
|
||||
} else if ( cDef.minValue == null ) {
|
||||
this.uocConstantSingleBound.execute( cDef.name , cDef.description , cDef.category , cDef.defaultValue ,
|
||||
cDef.maxValue , false );
|
||||
} else {
|
||||
this.uocConstantTwoBounds.execute( cDef.name , cDef.description , cDef.category , cDef.defaultValue ,
|
||||
cDef.minValue , cDef.maxValue );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks that the descriptive parts of a constant data instance match the corresponding fields
|
||||
* in a constant definition instance.
|
||||
*
|
||||
* @param constant
|
||||
* the constant data instance
|
||||
* @param def
|
||||
* the definition
|
||||
* @return <code>true</code> if the descriptive parts are a match
|
||||
*/
|
||||
private boolean compareConstantDescription( Constant constant , ConstantDefinition def )
|
||||
{
|
||||
return constant.getCategory( ).equals( def.category ) && constant.getDescription( ).equals( def.description );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks that the bounds defined in a constant data instance are identical to the bounds from a
|
||||
* constant definition.
|
||||
*
|
||||
* @param constant
|
||||
* the constant data instance
|
||||
* @param def
|
||||
* the definition
|
||||
* @return <code>true</code> if the bounds match
|
||||
*/
|
||||
private boolean compareConstantBounds( Constant constant , ConstantDefinition def )
|
||||
{
|
||||
return constant.getMinValue( ) == def.minValue && constant.getMaxValue( ) == def.maxValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates a constant's value after bounds changed.
|
||||
*
|
||||
* This method, called when a constant data instance's bounds have been changed, verifies that
|
||||
* the constant's current value matches the new bounds and modifies it if that is required.
|
||||
*
|
||||
* @param constant
|
||||
* the constant data instance
|
||||
* @param def
|
||||
* the definition
|
||||
* @return <code>true</code> if the value was updated
|
||||
*/
|
||||
private boolean updateValue( Constant constant , ConstantDefinition def )
|
||||
{
|
||||
if ( def.minValue != null && def.minValue > constant.getValue( ) ) {
|
||||
constant.setValue( def.minValue );
|
||||
return true;
|
||||
} else if ( def.maxValue != null && def.maxValue < constant.getValue( ) ) {
|
||||
constant.setValue( def.maxValue );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notifies constant users that a specific constant value has been modified.
|
||||
*
|
||||
* @param changed
|
||||
* the constant's name
|
||||
*
|
||||
* @see #notify(Set)
|
||||
*/
|
||||
private void notify( String changed )
|
||||
{
|
||||
Set< String > temp = new HashSet< String >( );
|
||||
temp.add( changed );
|
||||
this.notify( temp );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notifies constant users that a set of constants has changed.
|
||||
*
|
||||
* Users of each specified constant are identified, and separated in two groups: users that have
|
||||
* not received their initial notification and for which initial notification must be attempted,
|
||||
* and already initialised users which will only receive updates on constants that actually
|
||||
* changed.
|
||||
*
|
||||
* @param changed
|
||||
* the names of the constants that have been changed.
|
||||
*
|
||||
* @see #notifyUpdate(ConstantsUser, Set)
|
||||
* @see #attemptFirstNotification(ConstantsUser)
|
||||
*/
|
||||
private void notify( Set< String > changed )
|
||||
{
|
||||
Map< ConstantsUser , Set< String >> notifyChange = new HashMap< ConstantsUser , Set< String > >( );
|
||||
Set< ConstantsUser > firstNotification = new HashSet< ConstantsUser >( );
|
||||
|
||||
// Build a map of users <-> notifications and a set of first notifications to attempt
|
||||
for ( String ccn : changed ) {
|
||||
Set< ConstantsUser > users = this.constantUsers.get( ccn );
|
||||
if ( users == null ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ( ConstantsUser user : users ) {
|
||||
if ( this.notifiedUsers.contains( user ) ) {
|
||||
Set< String > userChange = notifyChange.get( user );
|
||||
if ( userChange == null ) {
|
||||
userChange = new HashSet< String >( );
|
||||
notifyChange.put( user , userChange );
|
||||
}
|
||||
userChange.add( ccn );
|
||||
} else {
|
||||
firstNotification.add( user );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt first notifications
|
||||
for ( ConstantsUser user : firstNotification ) {
|
||||
this.attemptFirstNotification( user );
|
||||
}
|
||||
|
||||
// Send updates to the others
|
||||
for ( ConstantsUser user : notifyChange.keySet( ) ) {
|
||||
this.notifyUpdate( user , notifyChange.get( user ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notifies a constant user of changes.
|
||||
*
|
||||
* This method retrieves the values of the listed constants, then notifies the specified user
|
||||
* that they have changed. If an exception occurs in the user's notification method, an error is
|
||||
* written to the server's log.
|
||||
*
|
||||
* @param user
|
||||
* the constants user instance.
|
||||
* @param changed
|
||||
* the names of the constants for which a notification is to be sent.
|
||||
*/
|
||||
private void notifyUpdate( ConstantsUser user , Set< String > changed )
|
||||
{
|
||||
Map< String , Double > values = new HashMap< String , Double >( );
|
||||
|
||||
// Find updated values
|
||||
for ( String cn : changed ) {
|
||||
values.put( cn , this.registeredConstants.get( cn ).getValue( ) );
|
||||
}
|
||||
|
||||
// Send notification
|
||||
try {
|
||||
user.setConstants( false , values );
|
||||
} catch ( Throwable t ) {
|
||||
this.sysLog.log( LogLevel.ERROR , "updating constant values caused an exception" , t ).flush( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts initial notification of constants values to a constants user.
|
||||
*
|
||||
* This method is called when constants registered to a not-yet-initialised constants user
|
||||
* change. It attempts to retrieve the required values, returning directly if a constant is
|
||||
* missing. If all values exist, the initial notification is sent. Should an exception occur, it
|
||||
* is logged as an error and the method ends; otherwise the user is added to the set of
|
||||
* initialised users.
|
||||
*
|
||||
* @param user
|
||||
* the constants user to notify
|
||||
*/
|
||||
private void attemptFirstNotification( ConstantsUser user )
|
||||
{
|
||||
Map< String , Double > values = new HashMap< String , Double >( );
|
||||
|
||||
// Try to find all required values
|
||||
for ( String cn : this.userConstants.get( user ) ) {
|
||||
Constant cst = this.registeredConstants.get( cn );
|
||||
if ( cst == null ) {
|
||||
return;
|
||||
}
|
||||
values.put( cn , cst.getValue( ) );
|
||||
}
|
||||
|
||||
// Send initial notification
|
||||
try {
|
||||
user.setConstants( true , values );
|
||||
} catch ( Throwable t ) {
|
||||
this.sysLog.log( LogLevel.ERROR , "registering initial constant values caused an exception" , t ).flush( );
|
||||
return;
|
||||
}
|
||||
this.notifiedUsers.add( user );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the list of categories from the list of constants.
|
||||
*
|
||||
* This method updates the list of categories by going through all known constants, creating an
|
||||
* entry in the categories map for each category and associating it with the list of constants
|
||||
* it contains.
|
||||
*/
|
||||
private void updateCategories( )
|
||||
{
|
||||
if ( this.categories != null ) {
|
||||
return;
|
||||
}
|
||||
this.categories = new HashMap< String , List< Constant > >( );
|
||||
for ( Constant c : this.registeredConstants.values( ) ) {
|
||||
String cat = c.getCategory( );
|
||||
List< Constant > cContents;
|
||||
cContents = this.categories.get( cat );
|
||||
if ( cContents == null ) {
|
||||
cContents = new LinkedList< Constant >( );
|
||||
this.categories.put( cat , cContents );
|
||||
}
|
||||
cContents.add( c );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constants registration method.
|
||||
*
|
||||
* This method is responsible for updating the in-base constants data when new definitions are
|
||||
* registered. It will add new constants or update existing one. Finally, if some of the updated
|
||||
* constants are used by any constants user instance, it will send update notifications.
|
||||
*
|
||||
* @param definitions
|
||||
* the constants to register
|
||||
*/
|
||||
synchronized void registerConstants( Collection< ConstantDefinition > definitions )
|
||||
{
|
||||
Set< String > toNotify = new HashSet< String >( );
|
||||
final List< ConstantDefinition > toUpdate = new LinkedList< ConstantDefinition >( );
|
||||
|
||||
for ( ConstantDefinition def : definitions ) {
|
||||
// Handle new definitions
|
||||
Constant constant = this.registeredConstants.get( def.name );
|
||||
if ( constant == null ) {
|
||||
toUpdate.add( def );
|
||||
toNotify.add( def.name );
|
||||
this.sysLog.log( LogLevel.DEBUG , "registering constant " + def.name );
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean sameDescription = this.compareConstantDescription( constant , def );
|
||||
boolean sameBounds = this.compareConstantBounds( constant , def );
|
||||
if ( sameDescription && sameBounds ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the value is correct if the bounds have been changed
|
||||
if ( !sameBounds && this.updateValue( constant , def ) ) {
|
||||
toNotify.add( def.name );
|
||||
}
|
||||
|
||||
toUpdate.add( def );
|
||||
this.sysLog.log( LogLevel.DEBUG , "updating definition of constant " + def.name );
|
||||
}
|
||||
|
||||
// Apply updates and notify users
|
||||
if ( !toUpdate.isEmpty( ) ) {
|
||||
this.sysLog.flush( );
|
||||
this.tTemplate.execute( new TransactionCallbackWithoutResult( ) {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
updateConstants( toUpdate );
|
||||
loadConstants( );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
if ( !toNotify.isEmpty( ) ) {
|
||||
this.notify( toNotify );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constants user registration
|
||||
*
|
||||
* This method registers (or re-registers) a constants user, indicating the constants used by
|
||||
* the instance and for which update notifications should be sent. Once the user has been
|
||||
* registered, an attempt at sending the initial notification is made.
|
||||
*
|
||||
* @param user
|
||||
* the constants user to register
|
||||
* @param constants
|
||||
* the names of the constants for which notifications are required
|
||||
*/
|
||||
synchronized void registerUser( ConstantsUser user , Set< String > constants )
|
||||
{
|
||||
// If this user was already registered, unregister it
|
||||
if ( this.userConstants.containsKey( user ) ) {
|
||||
this.unregisterUser( user );
|
||||
}
|
||||
|
||||
// Add user <-> constant maps
|
||||
this.userConstants.put( user , new HashSet< String >( constants ) );
|
||||
for ( String cn : constants ) {
|
||||
Set< ConstantsUser > users;
|
||||
users = this.constantUsers.get( cn );
|
||||
if ( users == null ) {
|
||||
users = new HashSet< ConstantsUser >( );
|
||||
this.constantUsers.put( cn , users );
|
||||
}
|
||||
users.add( user );
|
||||
}
|
||||
|
||||
// Try sending the user its initial notification
|
||||
this.attemptFirstNotification( user );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unregisters a constants user.
|
||||
*
|
||||
* This method removes a constants user from the manager, preventing further notifications to be
|
||||
* sent.
|
||||
*
|
||||
* @param user
|
||||
* the constants user to unregister
|
||||
*/
|
||||
synchronized void unregisterUser( ConstantsUser user )
|
||||
{
|
||||
Set< String > constants = this.userConstants.get( user );
|
||||
if ( constants == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.userConstants.remove( user );
|
||||
this.notifiedUsers.remove( user );
|
||||
for ( String constant : constants ) {
|
||||
Set< ConstantsUser > users = this.constantUsers.get( constant );
|
||||
users.remove( user );
|
||||
if ( users.isEmpty( ) ) {
|
||||
this.constantUsers.remove( constant );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the names of the constant categories.
|
||||
*
|
||||
* This method will make sure the constant categories map is up-to-date, then return a copy of
|
||||
* its keys.
|
||||
*
|
||||
* @return the names of all constant categories
|
||||
*/
|
||||
synchronized Set< String > getCategories( )
|
||||
{
|
||||
this.updateCategories( );
|
||||
return new HashSet< String >( this.categories.keySet( ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accesses the constant data instances from a category.
|
||||
*
|
||||
* This method will make sure the constant categories map is up-to-date, then return a copy of
|
||||
* the list corresponding to the specified category. If the category is not present in the map,
|
||||
* <code>null</code> will be returned.
|
||||
*
|
||||
* @param cat
|
||||
* the category's name
|
||||
* @return the list of constant data instances, or <code>null</code> if the category is
|
||||
* undefined
|
||||
*/
|
||||
synchronized List< Constant > getCategory( String cat )
|
||||
{
|
||||
this.updateCategories( );
|
||||
List< Constant > listCst = this.categories.get( cat );
|
||||
if ( listCst != null ) {
|
||||
return new LinkedList< Constant >( listCst );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modifies the value of a single constant.
|
||||
*
|
||||
* This method is called by administrative sessions in order to modify a single constant. It
|
||||
* checks that the constant exists and that its new value is valid, then updates the database
|
||||
* entry. Once this is done, it notifies the constant's users and returns the previous value.
|
||||
*
|
||||
* @param name
|
||||
* the name of the constant to modify
|
||||
* @param value
|
||||
* the constant's new value
|
||||
* @return the constant's previous value
|
||||
* @throws UnknownConstantError
|
||||
* if the specified constant does not exist
|
||||
* @throws InvalidConstantValue
|
||||
* if the specified value is out of bounds
|
||||
*/
|
||||
synchronized Double setValue( final String name , final Double value , final int admin )
|
||||
throws UnknownConstantError , InvalidConstantValue
|
||||
{
|
||||
// Check name
|
||||
Constant c = this.registeredConstants.get( name );
|
||||
if ( c == null ) {
|
||||
throw new UnknownConstantError( name );
|
||||
}
|
||||
|
||||
// Check value
|
||||
if ( c.getMinValue( ) != null && value < c.getMinValue( ) || c.getMaxValue( ) != null
|
||||
&& value > c.getMaxValue( ) ) {
|
||||
throw new InvalidConstantValue( );
|
||||
}
|
||||
|
||||
// Store constant
|
||||
try {
|
||||
this.tTemplate.execute( new TransactionCallbackWithoutResult( ) {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
fSetConstant.execute( name , value.floatValue( ) , admin );
|
||||
}
|
||||
|
||||
} );
|
||||
} catch ( RuntimeException t ) {
|
||||
this.sysLog.log( LogLevel.ERROR , "could not update constant " + c.getName( ) , t ).flush( );
|
||||
throw t;
|
||||
}
|
||||
|
||||
// Update in-memory copy
|
||||
Double old = c.getValue( );
|
||||
c.setValue( value );
|
||||
|
||||
// Notify constant users
|
||||
this.notify( c.getName( ) );
|
||||
|
||||
return old;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.deepclone.lw.interfaces.eventlog.Logger;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantDefinition;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantsAdministration;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantsManager;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantsUser;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The constants manager bean initialises a {@link ConstantsData} instance, to which it delegates
|
||||
* all operations.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
public class ConstantsManagerBean
|
||||
implements ConstantsManager , InitializingBean
|
||||
{
|
||||
|
||||
/** Transaction manager interface */
|
||||
private TransactionTemplate tTemplate;
|
||||
|
||||
/** Logger bean */
|
||||
private Logger logger;
|
||||
|
||||
/** Shared constants data instance */
|
||||
private ConstantsData data;
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
|
||||
@Autowired( required = true )
|
||||
public void setDataSource( DataSource dataSource )
|
||||
{
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the transaction manager interface (DI)
|
||||
*
|
||||
* @param transactionManager
|
||||
* the transaction manager
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setTransactionManager( PlatformTransactionManager transactionManager )
|
||||
{
|
||||
this.tTemplate = new TransactionTemplate( transactionManager );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the logger bean interface (DI)
|
||||
*
|
||||
* @param logger
|
||||
* the logger bean
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setLogger( Logger logger )
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
/** Creates the data storage and manipulation instance once the bean is ready */
|
||||
@Override
|
||||
public void afterPropertiesSet( )
|
||||
{
|
||||
this.data = new ConstantsData( this.dataSource , this.tTemplate , this.logger );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ConstantsManager interface */
|
||||
@Override
|
||||
public void registerConstants( Collection< ConstantDefinition > definitions )
|
||||
{
|
||||
this.data.registerConstants( definitions );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ConstantsManager interface */
|
||||
@Override
|
||||
public void registerUser( ConstantsUser user , Set< String > constants )
|
||||
{
|
||||
this.data.registerUser( user , constants );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ConstantsManager interface */
|
||||
@Override
|
||||
public void unregisterUser( ConstantsUser user )
|
||||
{
|
||||
this.data.unregisterUser( user );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ConstantsManager interface */
|
||||
@Override
|
||||
public ConstantsAdministration getAdminSession( int admin )
|
||||
{
|
||||
return new ConstantsAdministrationImpl( this.data , admin );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.deepclone.lw.interfaces.sys.ConstantDefinition;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantsManager;
|
||||
|
||||
|
||||
|
||||
public class ConstantsRegistrarBean
|
||||
{
|
||||
|
||||
@Autowired( required = true )
|
||||
public void setConstantsManager( ConstantsManager cm )
|
||||
{
|
||||
List< ConstantDefinition > defs = new LinkedList< ConstantDefinition >( );
|
||||
String cDesc;
|
||||
|
||||
// Default maximal age for log entries = 1 week
|
||||
double oneWeek = 60 * 60 * 24 * 7;
|
||||
|
||||
// Initial values
|
||||
cDesc = "Initial cash for new empires.";
|
||||
defs.add( new ConstantDefinition( "game.initialCash" , "Initial values" , cDesc , 2000.0 , 0.0 , true ) );
|
||||
cDesc = "Initial credits for new accounts.";
|
||||
defs.add( new ConstantDefinition( "game.initialCredits" , "Initial values" , cDesc , 0.0 , 0.0 , true ) );
|
||||
|
||||
// Misc. game-related values
|
||||
cDesc = "Game updates - batch size.";
|
||||
defs.add( new ConstantDefinition( "game.batchSize" , "Game (misc)" , cDesc , 20.0 , 1.0 , true ) );
|
||||
cDesc = "Population growth factor.";
|
||||
defs.add( new ConstantDefinition( "game.growthFactor" , "Game (misc)" , cDesc , 50.0 , 1.0 , true ) );
|
||||
cDesc = "Increase to the population growth factor caused by reanimation centres.";
|
||||
defs.add( new ConstantDefinition( "game.growthFactor.rCentre" , "Game (misc)" , cDesc , 10.0 , 1.0 , true ) );
|
||||
cDesc = "Time to abandon a planet (in ticks).";
|
||||
defs.add( new ConstantDefinition( "game.timeToAbandon" , "Game (misc)" , cDesc , 1440.0 , 1.0 , true ) );
|
||||
cDesc = "Fleet damage ratio per day when debt and upkeep are equal.";
|
||||
defs.add( new ConstantDefinition( "game.debt.fleet" , "Game (misc)" , cDesc , 0.3 , 0.001 , 0.999 ) );
|
||||
cDesc = "Buildings damage ratio per day when debt and upkeep are equal.";
|
||||
defs.add( new ConstantDefinition( "game.debt.buildings" , "Game (misc)" , cDesc , 0.1 , 0.001 , 0.999 ) );
|
||||
|
||||
// Universe
|
||||
cDesc = "Ratio of free planets below which universe expansion is triggered.";
|
||||
defs.add( new ConstantDefinition( "game.universe.minFreeRatio" , "Universe" , cDesc , 0.1 , 0.02 , 0.98 ) );
|
||||
cDesc = "Amount of pictures available to generate planets.";
|
||||
defs.add( new ConstantDefinition( "game.universe.pictures" , "Universe" , cDesc , 200.0 , 1.0 , true ) );
|
||||
cDesc = "Initial population on first-generation planets.";
|
||||
defs.add( new ConstantDefinition( "game.universe.initialPopulation" , "Universe" , cDesc , 2000.0 , 1000.0 ,
|
||||
true ) );
|
||||
cDesc = "Initial universe size (offset relative to the centre).";
|
||||
defs.add( new ConstantDefinition( "game.universe.initialSize" , "Universe" , cDesc , 1.0 , 1.0 , true ) );
|
||||
|
||||
// Happiness
|
||||
String[] hcNames = {
|
||||
"noEmployment" , "employmentLimit" , "noDefence" , "defenceLimit" , "popPerDefencePoint" ,
|
||||
"idealEmpireSize" , "smallEmpire" , "eSizeLimit" , "strike" , "relativeChange" , "maxAbsoluteChange"
|
||||
};
|
||||
for ( int i = 0 ; i < hcNames.length ; i++ ) {
|
||||
hcNames[ i ] = "game.happiness." + hcNames[ i ];
|
||||
}
|
||||
String cat = "Happiness";
|
||||
cDesc = "Employment happiness level when there is no employment.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 0 ] , cat , cDesc , 0.7 , 0.05 , 0.99 ) );
|
||||
cDesc = "Employment ratio beyond which things get tough.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 1 ] , cat , cDesc , 2.0 , 1.1 , true ) );
|
||||
cDesc = "Defence happiness level when there is no defence.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 2 ] , cat , cDesc , 0.5 , 0.05 , 0.99 ) );
|
||||
cDesc = "Defence ratio beyond which things get tough.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 3 ] , cat , cDesc , 4.0 , 1.1 , true ) );
|
||||
cDesc = "Population covered by a single defence power point.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 4 ] , cat , cDesc , 5.0 , 1.0 , true ) );
|
||||
cDesc = "Ideal empire size.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 5 ] , cat , cDesc , 10.0 , 2.0 , true ) );
|
||||
cDesc = "Happiness level for 1-planet empires.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 6 ] , cat , cDesc , 0.9 , 0.5 , true ) );
|
||||
cDesc = "Empire size limit (relative to the ideal size) beyond which things get tough.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 7 ] , cat , cDesc , 2.0 , 1.1 , true ) );
|
||||
cDesc = "Happiness level below which strikes begin.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 8 ] , cat , cDesc , 0.25 , 0.0 , 1.0 ) );
|
||||
cDesc = "Happiness change at each update, relative to the size of the population.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 9 ] , cat , cDesc , 0.001 , 0.00001 , 0.99999 ) );
|
||||
cDesc = "Maximal population units for which happiness will change.";
|
||||
defs.add( new ConstantDefinition( hcNames[ 10 ] , cat , cDesc , 1000.0 , 1.0 , true ) );
|
||||
|
||||
// Work and income
|
||||
String[] wcNames = {
|
||||
"population" , "factory" , "strikeEffect" , "wuPerPopUnit" , "destructionRecovery" , "destructionWork" ,
|
||||
"rpPerPopUnit" , "cancelRecovery"
|
||||
};
|
||||
for ( int i = 0 ; i < wcNames.length ; i++ ) {
|
||||
wcNames[ i ] = "game.work." + wcNames[ i ];
|
||||
}
|
||||
cat = "Work & income";
|
||||
cDesc = "Daily income per population unit.";
|
||||
defs.add( new ConstantDefinition( wcNames[ 0 ] , cat , cDesc , 0.2 , 0.01 , true ) );
|
||||
cDesc = "Daily income per factory production unit.";
|
||||
defs.add( new ConstantDefinition( wcNames[ 1 ] , cat , cDesc , 250.0 , 0.01 , true ) );
|
||||
cDesc = "Proportion of the base income that disappears if the whole population is on strike.";
|
||||
defs.add( new ConstantDefinition( wcNames[ 2 ] , cat , cDesc , 1.0 , 0.0 , 1.0 ) );
|
||||
cDesc = "Work units generated by each population unit.";
|
||||
defs.add( new ConstantDefinition( wcNames[ 3 ] , cat , cDesc , 0.01 , 0.001 , true ) );
|
||||
cDesc = "Proportion of a building's cost that is recovered when it is destroyed.";
|
||||
defs.add( new ConstantDefinition( wcNames[ 4 ] , cat , cDesc , 0.1 , 0.01 , 0.99 ) );
|
||||
cDesc = "Proportion of a building's construction work units required to destroy it";
|
||||
defs.add( new ConstantDefinition( wcNames[ 5 ] , cat , cDesc , 0.25 , 0.01 , 1.0 ) );
|
||||
cDesc = "Research points per population unit.";
|
||||
defs.add( new ConstantDefinition( wcNames[ 6 ] , cat , cDesc , 0.50 , 0.01 , true ) );
|
||||
cDesc = "Proportion of queue investments that is recovered when flushing the queue.";
|
||||
defs.add( new ConstantDefinition( wcNames[ 7 ] , cat , cDesc , 0.1 , 0.01 , 1.0 ) );
|
||||
|
||||
// Vacation mode
|
||||
cDesc = "Initial vacation credits.";
|
||||
defs.add( new ConstantDefinition( "vacation.initial" , "Vacation mode" , cDesc , 4320.0 , 0.0 , true ) );
|
||||
cDesc = "Delay before vacation mode is activated (seconds).";
|
||||
defs.add( new ConstantDefinition( "vacation.delay" , "Vacation mode" , cDesc , 21600.0 , 3600.0 , true ) );
|
||||
cDesc = "Maximal vacation credits.";
|
||||
defs.add( new ConstantDefinition( "vacation.max" , "Vacation mode" , cDesc , 259200.0 , 10000.0 , true ) );
|
||||
cDesc = "Vacation cost (credits / minute).";
|
||||
defs.add( new ConstantDefinition( "vacation.cost" , "Vacation mode" , cDesc , 3.0 , 2.0 , 20.0 ) );
|
||||
cDesc = "Income/upkeep divider used when vacation mode is active.";
|
||||
defs.add( new ConstantDefinition( "vacation.cashDivider" , "Vacation mode" , cDesc , 3.0 , 1.1 , 10.0 ) );
|
||||
cDesc = "Research points divider used when vacation mode is active.";
|
||||
defs.add( new ConstantDefinition( "vacation.researchDivider" , "Vacation mode" , cDesc , 10.0 , 1.1 , 50.0 ) );
|
||||
|
||||
// Map names
|
||||
cDesc = "Minimal delay between map object renaming.";
|
||||
defs.add( new ConstantDefinition( "map.names.minDelay" , "Map names" , cDesc , 15.0 , 1.0 , true ) );
|
||||
cDesc = "Units for the minimal delay between map object renaming (seconds).";
|
||||
defs.add( new ConstantDefinition( "map.names.minDelay.units" , "Map names" , cDesc , 86400.0 , 1.0 , true ) );
|
||||
|
||||
// Log clean-up
|
||||
cDesc = "Maximal age of administration log entries, in seconds.";
|
||||
defs.add( new ConstantDefinition( "log.maxAge.admin" , "Logs" , cDesc , oneWeek , 1.0 , true ) );
|
||||
cDesc = "Maximal age of account log entries, in seconds.";
|
||||
defs.add( new ConstantDefinition( "log.maxAge.users" , "Logs" , cDesc , oneWeek , 1.0 , true ) );
|
||||
cDesc = "Maximal age of system log entries, in seconds.";
|
||||
defs.add( new ConstantDefinition( "log.maxAge.sys" , "Logs" , cDesc , oneWeek , 1.0 , true ) );
|
||||
|
||||
// Battles
|
||||
cDesc = "Amount of ticks before a battle reaches full intensity.";
|
||||
defs.add( new ConstantDefinition( "game.battle.timeToFullIntensity" , "Battles" , cDesc , 30.0 , 0.0 , true ) );
|
||||
cDesc = "Initial intensity of a battle.";
|
||||
defs.add( new ConstantDefinition( "game.battle.initialIntensity" , "Battles" , cDesc , 0.25 , 0.0 , 1.0 ) );
|
||||
cDesc = "Defence bonus.";
|
||||
defs.add( new ConstantDefinition( "game.battle.defenceBonus" , "Battles" , cDesc , 0.1 , 0.0 , true ) );
|
||||
cDesc = "Damage / power unit / minute";
|
||||
defs.add( new ConstantDefinition( "game.battle.damage" , "Battles" , cDesc , 0.01 , 0.001 , true ) );
|
||||
cDesc = "Proportion of random damage variation.";
|
||||
defs.add( new ConstantDefinition( "game.battle.randomDamage" , "Battles" , cDesc , 0.05 , 0.0 , 0.9 ) );
|
||||
|
||||
// Ticker
|
||||
cDesc = "Interval between ticks with the highest frequency, in milliseconds.";
|
||||
defs.add( new ConstantDefinition( "ticker.interval" , "Ticker" , cDesc , 5000.0 , 1000.0 , true ) );
|
||||
|
||||
// Accounts
|
||||
cDesc = "Minimal interval between address change requests (seconds)";
|
||||
defs.add( new ConstantDefinition( "accounts.acrDelay" , "Accounts" , cDesc , 14400.0 , 1.0 , true ) );
|
||||
cDesc = "Minimal interval between password recovery requests (seconds)";
|
||||
defs.add( new ConstantDefinition( "accounts.prrDelay" , "Accounts" , cDesc , 8800.0 , 1.0 , true ) );
|
||||
cDesc = "Time before a new or reactivated account is deleted / disabled (seconds)";
|
||||
defs.add( new ConstantDefinition( "accounts.cacDelay" , "Accounts" , cDesc , 86400.0 , 3600.0 , true ) );
|
||||
cDesc = "Delay between clicking the 'Quit' button and account de-activation (seconds)";
|
||||
defs.add( new ConstantDefinition( "accounts.quitDelay" , "Accounts" , cDesc , 86400.0 , 3600.0 , true ) );
|
||||
cDesc = "Delay between a ban and the deletion of the player's empire (seconds)";
|
||||
defs.add( new ConstantDefinition( "accounts.banDelay" , "Accounts" , cDesc , 178000.0 , 3600.0 , true ) );
|
||||
cDesc = "Delay before a ban request expires (seconds)";
|
||||
defs.add( new ConstantDefinition( "accounts.banExpiration" , "Accounts" , cDesc , oneWeek , 3600.0 , true ) );
|
||||
|
||||
// Accounts - warnings
|
||||
cDesc = "Amount of warnings that triggers an automatic ban request.";
|
||||
defs.add( new ConstantDefinition( "accounts.warnings.autoBan" , "Accounts - Warnings" , cDesc , 3.0 , 1.0 , true ) );
|
||||
cDesc = "Period after a warning is received during which additional warnings will be ignored (seconds).";
|
||||
defs.add( new ConstantDefinition( "accounts.warnings.grace" , "Accounts - Warnings" , cDesc , 7200.0 , 60.0 , true ) );
|
||||
cDesc = "Time after which warnings are decreased (expressed in units as defined by a.w.expiration.units).";
|
||||
defs.add( new ConstantDefinition( "accounts.warnings.expiration" , "Accounts - Warnings" , cDesc , 60.0 , 1.0 , true ) );
|
||||
cDesc = "Units used to express warning expiration time (seconds).";
|
||||
defs.add( new ConstantDefinition( "accounts.warnings.expiration.units" , "Accounts - Warnings" , cDesc , 86400.0 , 1.0 , true ) );
|
||||
|
||||
// Account inactivity
|
||||
cDesc = "Time units (seconds)";
|
||||
defs.add( new ConstantDefinition( "accounts.inactivity.units" , "Accounts - Inactivity" , cDesc , oneWeek , 3600.0 , true ) );
|
||||
cDesc = "Time after which the inactivity warning e-mail is to be sent, expressed using units defined by a.i.units.";
|
||||
defs.add( new ConstantDefinition( "accounts.inactivity.warningMail" , "Accounts - Inactivity" , cDesc , 3.0 , 1.0 , true ) );
|
||||
cDesc = "Time between the inactivity warning e-mail and actual account deletion, expressed using units defined by a.i.units.";
|
||||
defs.add( new ConstantDefinition( "accounts.inactivity.deletion" , "Accounts - Inactivity" , cDesc , 1.0 , 1.0 , true ) );
|
||||
|
||||
// Bug reports
|
||||
cDesc = "Amount of credits granted for low priority bug reports.";
|
||||
defs.add( new ConstantDefinition( "bugtracker.lowCredits" , "Bug tracking system" , cDesc , 1.0 , 1.0 , true ) );
|
||||
cDesc = "Amount of credits granted for normal bug reports.";
|
||||
defs.add( new ConstantDefinition( "bugtracker.mediumCredits" , "Bug tracking system" , cDesc , 2.0 , 1.0 , true ) );
|
||||
cDesc = "Amount of credits granted for critical bug reports.";
|
||||
defs.add( new ConstantDefinition( "bugtracker.highCredits" , "Bug tracking system" , cDesc , 3.0 , 1.0 , true ) );
|
||||
|
||||
cm.registerConstants( defs );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.deepclone.lw.cmd.admin.users.SessionTerminationType;
|
||||
import com.deepclone.lw.interfaces.session.ServerSession;
|
||||
import com.deepclone.lw.interfaces.session.SessionTypeDefiner;
|
||||
import com.deepclone.lw.session.Command;
|
||||
import com.deepclone.lw.session.CommandResponse;
|
||||
import com.deepclone.lw.session.SessionCommandException;
|
||||
import com.deepclone.lw.session.SessionIdentifierException;
|
||||
import com.deepclone.lw.session.SessionReference;
|
||||
import com.deepclone.lw.session.SessionStateException;
|
||||
import com.deepclone.lw.utils.RandomStringGenerator;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Session data.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
class ServerSessionData
|
||||
implements ServerSession
|
||||
{
|
||||
|
||||
/** Data store */
|
||||
private Map< String , Object > storage = new HashMap< String , Object >( );
|
||||
|
||||
/** Session identifier */
|
||||
private String identifier;
|
||||
|
||||
/** Definer for the session's type */
|
||||
private SessionTypeDefiner definer;
|
||||
|
||||
/** Authentication challenge generator */
|
||||
private RandomStringGenerator challengeGenerator;
|
||||
|
||||
/** Last authentication challenge */
|
||||
private String lastChallenge;
|
||||
|
||||
/** Session expiration date */
|
||||
private Date expire;
|
||||
|
||||
/** Termination reason */
|
||||
SessionTerminationType termination = null;
|
||||
|
||||
/** IPv4 address of the client */
|
||||
private InetAddress address;
|
||||
|
||||
/** Client type identifier */
|
||||
private String client;
|
||||
|
||||
|
||||
/**
|
||||
* Initialises a new session.
|
||||
*
|
||||
* @param generator
|
||||
* authentication challenge generator
|
||||
* @param definer
|
||||
* session type definer
|
||||
* @param client
|
||||
* type of the client interface initiating the session
|
||||
* @param address
|
||||
* IP address the session is being initiated from
|
||||
*/
|
||||
public ServerSessionData( RandomStringGenerator generator , SessionTypeDefiner definer , String client ,
|
||||
InetAddress address )
|
||||
{
|
||||
this.challengeGenerator = generator;
|
||||
this.definer = definer;
|
||||
this.expire = new Date( new Date( ).getTime( ) + 5000L );
|
||||
this.definer.initialise( this );
|
||||
this.client = client;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the session's identifier
|
||||
*
|
||||
* @param id
|
||||
* the session's identifier
|
||||
*/
|
||||
public void setIdentifier( String id )
|
||||
{
|
||||
this.identifier = id;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ServerSession interface */
|
||||
@Override
|
||||
public String getIdentifier( )
|
||||
{
|
||||
return this.identifier;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ServerSession interface */
|
||||
@Override
|
||||
public String getClient( )
|
||||
{
|
||||
return this.client;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ServerSession interface */
|
||||
@Override
|
||||
public InetAddress getAddress( )
|
||||
{
|
||||
return this.address;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ServerSession interface */
|
||||
@Override
|
||||
public String getChallenge( )
|
||||
{
|
||||
return this.lastChallenge;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ServerSession interface */
|
||||
@Override
|
||||
public void setExpirationDate( Date date )
|
||||
{
|
||||
this.expire = date;
|
||||
}
|
||||
|
||||
|
||||
/** @return the session's expiration date */
|
||||
public Date getExpirationDate( )
|
||||
{
|
||||
return this.expire;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the termination type and handles termination
|
||||
*
|
||||
* @param termination
|
||||
* type of session termination
|
||||
*/
|
||||
synchronized public void handleTermination( SessionTerminationType termination )
|
||||
{
|
||||
if ( this.termination == null ) {
|
||||
this.termination = termination;
|
||||
this.definer.terminate( this , termination );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ServerSession interface */
|
||||
@Override
|
||||
public SessionTerminationType getTerminationType( )
|
||||
{
|
||||
return this.termination;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ServerSession interface */
|
||||
@Override
|
||||
public void terminate( )
|
||||
{
|
||||
this.handleTermination( SessionTerminationType.MANUAL );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void terminate( SessionTerminationType reason )
|
||||
{
|
||||
this.handleTermination( reason );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ServerSession interface */
|
||||
@Override
|
||||
public void put( String key , Object value )
|
||||
{
|
||||
if ( value == null ) {
|
||||
this.storage.remove( key );
|
||||
} else {
|
||||
this.storage.put( key , value );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documented in ServerSession interface */
|
||||
@Override
|
||||
public < T > T get( String key , Class< T > type )
|
||||
{
|
||||
Object obj = this.storage.get( key );
|
||||
try {
|
||||
return ( obj == null ) ? null : type.cast( obj );
|
||||
} catch ( ClassCastException e ) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a {@link SessionReference} from the session's current state.
|
||||
*
|
||||
* @return the new session reference
|
||||
*/
|
||||
synchronized public SessionReference toSessionReference( )
|
||||
{
|
||||
if ( this.termination != null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean auth = this.definer.isAuthenticated( this );
|
||||
String extra;
|
||||
if ( auth ) {
|
||||
extra = this.definer.getState( this );
|
||||
} else {
|
||||
extra = this.challengeGenerator.generate( );
|
||||
this.lastChallenge = extra;
|
||||
}
|
||||
|
||||
return new SessionReference( this.identifier , this.definer.getName( ) , auth , extra );
|
||||
}
|
||||
|
||||
|
||||
/** Attempts to authenticate the session */
|
||||
synchronized public void authenticate( String identifier , String sha1Hash , String md5Hash )
|
||||
throws SessionStateException , SessionIdentifierException
|
||||
{
|
||||
if ( this.termination != null ) {
|
||||
throw new SessionIdentifierException( this.identifier );
|
||||
}
|
||||
this.definer.authenticate( this , identifier , sha1Hash , md5Hash );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to execute a command on the session
|
||||
*/
|
||||
synchronized public CommandResponse execute( Command command )
|
||||
throws SessionStateException , SessionCommandException , SessionIdentifierException
|
||||
{
|
||||
if ( this.termination != null ) {
|
||||
throw new SessionIdentifierException( this.identifier );
|
||||
}
|
||||
return this.definer.execute( this , command );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,396 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import com.deepclone.lw.cmd.admin.logs.LogLevel;
|
||||
import com.deepclone.lw.cmd.admin.users.SessionTerminationType;
|
||||
import com.deepclone.lw.interfaces.eventlog.Logger;
|
||||
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
|
||||
import com.deepclone.lw.interfaces.session.*;
|
||||
import com.deepclone.lw.interfaces.sys.Ticker;
|
||||
import com.deepclone.lw.session.*;
|
||||
import com.deepclone.lw.utils.RandomStringGenerator;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Session management bean implementation.
|
||||
*
|
||||
* <p>
|
||||
* This class is the implementation of the session management bean. It is responsible for creating
|
||||
* sessions, using its collection of type definers, for accessing the sessions, and for making them
|
||||
* expire as required.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
public class SessionManagerBean
|
||||
implements SessionManager , DisposableBean
|
||||
{
|
||||
|
||||
/** Session type definers, by name */
|
||||
private final Map< String , WeakReference< SessionTypeDefiner > > types = new HashMap< String , WeakReference< SessionTypeDefiner > >( );
|
||||
|
||||
/** Sessions, by identifier */
|
||||
private final Map< String , ServerSessionData > sessions = new HashMap< String , ServerSessionData >( );
|
||||
|
||||
/** Random string generator for session identifiers */
|
||||
private RandomStringGenerator sessionIDGenerator;
|
||||
|
||||
/** Random string generator for authentication challenges */
|
||||
private RandomStringGenerator challengeGenerator;
|
||||
|
||||
/** Session clean-up task, registered to the {@link Ticker} */
|
||||
private Runnable cleanupTask;
|
||||
|
||||
/** System logger for the session manager */
|
||||
private SystemLogger logger;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the generator for session identifiers (DI)
|
||||
*
|
||||
* @param rsg
|
||||
* reference to the random string generator to use for session identifiers
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
@Qualifier( "sessionIdentifiers" )
|
||||
public void setSessionIDGenerator( RandomStringGenerator rsg )
|
||||
{
|
||||
this.sessionIDGenerator = rsg;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the generator for authentication challenges (DI)
|
||||
*
|
||||
* @param rsg
|
||||
* reference to the random string generator to use for authentication challenges
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
@Qualifier( "authChallenges" )
|
||||
public void setChallangeGenerator( RandomStringGenerator rsg )
|
||||
{
|
||||
this.challengeGenerator = rsg;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the session clean-up task (DI)
|
||||
*
|
||||
* @param ticker
|
||||
* reference to the ticker bean
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setTicker( Ticker ticker )
|
||||
{
|
||||
this.cleanupTask = new Runnable( ) {
|
||||
|
||||
@Override
|
||||
public void run( )
|
||||
{
|
||||
purgeSessions( );
|
||||
}
|
||||
|
||||
};
|
||||
ticker.registerTask( Ticker.Frequency.HIGH , "Sessions clean-up" , this.cleanupTask );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the system logger (DI)
|
||||
*
|
||||
* @param logger
|
||||
* reference to the logger bean
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setLogger( Logger logger )
|
||||
{
|
||||
this.logger = logger.getSystemLogger( "SessionManager" );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the reference to the clean-up task.
|
||||
*/
|
||||
@Override
|
||||
public void destroy( )
|
||||
{
|
||||
this.cleanupTask = null;
|
||||
synchronized ( this.sessions ) {
|
||||
for ( ServerSessionData data : this.sessions.values( ) ) {
|
||||
try {
|
||||
this.logger.log( LogLevel.DEBUG , "expiring '" + data.getIdentifier( ) + "'" ).flush( );
|
||||
data.handleTermination( SessionTerminationType.EXPIRED );
|
||||
} catch ( RuntimeException e ) {
|
||||
this.logger.log( LogLevel.ERROR , "error while terminating '" + data.getIdentifier( ) + "'" , e )
|
||||
.flush( );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds the session type definer corresponding to a type name.
|
||||
*
|
||||
* @param type
|
||||
* the name of the session type
|
||||
* @return the session type definer, or <code>null</code> if it doesn't exist
|
||||
*/
|
||||
private SessionTypeDefiner getTypeDefiner( String type )
|
||||
{
|
||||
SessionTypeDefiner definer = null;
|
||||
|
||||
synchronized ( this.types ) {
|
||||
WeakReference< SessionTypeDefiner > ref = this.types.get( type );
|
||||
if ( ref != null ) {
|
||||
definer = ref.get( );
|
||||
if ( definer == null ) {
|
||||
this.types.remove( type );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return definer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocates a session identifier.
|
||||
*
|
||||
* <p>
|
||||
* This method generates an unique session identifier for the specified session, setting the
|
||||
* session's identifier and storing it in the {@link #sessions} map.
|
||||
*
|
||||
* @param data
|
||||
* the session that needs to be initialised
|
||||
*/
|
||||
private void allocateSessionIdentifier( ServerSessionData data )
|
||||
{
|
||||
synchronized ( this.sessions ) {
|
||||
String id;
|
||||
do {
|
||||
id = this.sessionIDGenerator.generate( );
|
||||
} while ( this.sessions.containsKey( id ) );
|
||||
data.setIdentifier( id );
|
||||
this.sessions.put( id , data );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves a session from an identifier.
|
||||
*
|
||||
* <p>
|
||||
* This method attempts to retrieve a session from the {@link #sessions} map. If the session
|
||||
* exists but is expired, it will be removed and the method will behave as if it didn't exist.
|
||||
*
|
||||
* @param id
|
||||
* the session's identifier
|
||||
* @return the session's data
|
||||
* @throws SessionIdentifierException
|
||||
* if the session doesn't exist or has expired.
|
||||
*/
|
||||
private ServerSessionData getSession( String id )
|
||||
throws SessionIdentifierException
|
||||
{
|
||||
ServerSessionData data;
|
||||
synchronized ( this.sessions ) {
|
||||
data = this.sessions.get( id );
|
||||
if ( data == null ) {
|
||||
this.logger.log( LogLevel.INFO , "session '" + id + "' not found" ).flush( );
|
||||
throw new SessionIdentifierException( id );
|
||||
}
|
||||
if ( data.getExpirationDate( ).before( new Date( ) ) ) {
|
||||
this.logger.log( LogLevel.INFO , "session '" + id + "' found but expired" ).flush( );
|
||||
ServerSessionData session = this.sessions.remove( id );
|
||||
session.handleTermination( SessionTerminationType.EXPIRED );
|
||||
throw new SessionIdentifierException( id );
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes expired sessions.
|
||||
*
|
||||
* <p>
|
||||
* This method goes through all registered sessions, removing them from the {@link #sessions}
|
||||
* map if they have expired.
|
||||
*/
|
||||
private void purgeSessions( )
|
||||
{
|
||||
Date now = new Date( );
|
||||
List< ServerSessionData > toRemove = new LinkedList< ServerSessionData >( );
|
||||
synchronized ( this.sessions ) {
|
||||
for ( ServerSessionData session : this.sessions.values( ) ) {
|
||||
if ( session.getExpirationDate( ).before( now ) ) {
|
||||
toRemove.add( session );
|
||||
}
|
||||
}
|
||||
|
||||
for ( ServerSessionData session : toRemove ) {
|
||||
this.sessions.remove( session.getIdentifier( ) );
|
||||
}
|
||||
}
|
||||
|
||||
for ( ServerSessionData session : toRemove ) {
|
||||
this.handleTermination( session , SessionTerminationType.EXPIRED );
|
||||
}
|
||||
|
||||
if ( !toRemove.isEmpty( ) ) {
|
||||
this.logger.log( LogLevel.TRACE , toRemove.size( ) + " session(s) removed" ).flush( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleTermination( ServerSessionData session , SessionTerminationType reason )
|
||||
{
|
||||
try {
|
||||
session.handleTermination( reason );
|
||||
} catch ( RuntimeException e ) {
|
||||
this.logger.log( LogLevel.ERROR , "error while terminating '" + session.getIdentifier( ) + "'" , e )
|
||||
.flush( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documented in SessionManager interface */
|
||||
@Override
|
||||
public void registerSessionType( SessionTypeDefiner definer )
|
||||
{
|
||||
synchronized ( this.types ) {
|
||||
this.types.put( definer.getName( ) , new WeakReference< SessionTypeDefiner >( definer ) );
|
||||
}
|
||||
this.logger.log( LogLevel.INFO , "registered session type definer '" + definer.getName( ) + "'" ).flush( );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in SessionAccessor interface */
|
||||
@Override
|
||||
public SessionReference create( String type , String client , InetAddress address )
|
||||
throws SessionInternalException
|
||||
{
|
||||
SessionTypeDefiner definer = this.getTypeDefiner( type );
|
||||
if ( definer == null ) {
|
||||
this.logger.log( LogLevel.WARNING , "no such session type '" + type + "'" ).flush( );
|
||||
return null;
|
||||
}
|
||||
|
||||
ServerSessionData data;
|
||||
try {
|
||||
data = new ServerSessionData( this.challengeGenerator , definer , client , address );
|
||||
} catch ( RuntimeException e ) {
|
||||
this.logger.log( LogLevel.ERROR , "error while creating session with type '" + type + "'" , e ).flush( );
|
||||
throw new SessionInternalException( false , e );
|
||||
}
|
||||
|
||||
this.allocateSessionIdentifier( data );
|
||||
this.logger.log( LogLevel.INFO , "session " + data.getIdentifier( ) + " created (type " + type + ")" ).flush( );
|
||||
|
||||
return data.toSessionReference( );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in SessionAccessor interface */
|
||||
@Override
|
||||
public SessionReference authenticate( String session , String identifier , String sha1Hash , String md5Hash )
|
||||
throws SessionIdentifierException , SessionStateException , SessionInternalException
|
||||
{
|
||||
ServerSessionData data = this.getSession( session );
|
||||
this.logger.log( LogLevel.TRACE , "attempting to authenticate session '" + session + "'" ).flush( );
|
||||
|
||||
try {
|
||||
data.authenticate( identifier , sha1Hash , md5Hash );
|
||||
} catch ( SessionIdentifierException e ) {
|
||||
synchronized ( this.sessions ) {
|
||||
this.sessions.remove( session );
|
||||
}
|
||||
throw e;
|
||||
} catch ( RuntimeException e ) {
|
||||
this.logger.log( LogLevel.ERROR , "error while authenticating session '" + session + "'" , e ).flush( );
|
||||
throw new SessionInternalException( false , e );
|
||||
}
|
||||
|
||||
if ( data.getTerminationType( ) != null ) {
|
||||
synchronized ( this.sessions ) {
|
||||
this.logger.log( LogLevel.INFO , "terminated '" + session + "' during authentication" ).flush( );
|
||||
this.sessions.remove( session );
|
||||
}
|
||||
}
|
||||
|
||||
return data.toSessionReference( );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in SessionAccessor interface */
|
||||
@Override
|
||||
public SessionResponse executeCommand( String session , Command command )
|
||||
throws SessionIdentifierException , SessionStateException , SessionCommandException ,
|
||||
SessionInternalException
|
||||
{
|
||||
ServerSessionData data = this.getSession( session );
|
||||
this.logger.log( LogLevel.DEBUG , "executing command " + command.getClass( ) + " on '" + session + "'" )
|
||||
.flush( );
|
||||
|
||||
CommandResponse response;
|
||||
try {
|
||||
response = data.execute( command );
|
||||
} catch ( SessionIdentifierException e ) {
|
||||
synchronized ( this.sessions ) {
|
||||
this.sessions.remove( session );
|
||||
}
|
||||
throw e;
|
||||
} catch ( RuntimeException e ) {
|
||||
this.logger.log( LogLevel.ERROR , "error while executing command on '" + session + "'" , e ).flush( );
|
||||
throw new SessionInternalException( false , e );
|
||||
}
|
||||
|
||||
if ( data.getTerminationType( ) != null ) {
|
||||
this.logger.log( LogLevel.INFO , "terminated '" + session + "' during command execution" ).flush( );
|
||||
synchronized ( this.sessions ) {
|
||||
this.sessions.remove( session );
|
||||
}
|
||||
}
|
||||
|
||||
return new SessionResponse( data.toSessionReference( ) , response );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in SessionAccessor interface */
|
||||
@Override
|
||||
public void terminate( String session )
|
||||
throws SessionIdentifierException , SessionInternalException
|
||||
{
|
||||
ServerSessionData data;
|
||||
synchronized ( this.sessions ) {
|
||||
data = this.sessions.remove( session );
|
||||
}
|
||||
if ( data == null ) {
|
||||
throw new SessionIdentifierException( session );
|
||||
} else if ( data.getExpirationDate( ).before( new Date( ) ) ) {
|
||||
data.handleTermination( SessionTerminationType.EXPIRED );
|
||||
throw new SessionIdentifierException( session );
|
||||
}
|
||||
|
||||
try {
|
||||
data.handleTermination( SessionTerminationType.MANUAL );
|
||||
this.logger.log( LogLevel.INFO , "terminated '" + session + "'" ).flush( );
|
||||
} catch ( RuntimeException e ) {
|
||||
this.logger.log( LogLevel.ERROR , "error while terminating '" + session + "'" , e ).flush( );
|
||||
throw new SessionInternalException( false , e );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.SqlOutParameter;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.deepclone.lw.interfaces.sys.MaintenanceData;
|
||||
import com.deepclone.lw.interfaces.sys.MaintenanceStatusException;
|
||||
import com.deepclone.lw.interfaces.sys.SystemStatus;
|
||||
import com.deepclone.lw.interfaces.sys.TickStatusException;
|
||||
import com.deepclone.lw.sqld.sys.Status;
|
||||
import com.deepclone.lw.utils.StoredProc;
|
||||
|
||||
|
||||
|
||||
public class SystemStatusBean
|
||||
implements SystemStatus
|
||||
{
|
||||
|
||||
/** Database interface */
|
||||
private SimpleJdbcTemplate dTemplate;
|
||||
|
||||
/** Transaction template */
|
||||
private TransactionTemplate tTemplate;
|
||||
|
||||
/** System status record */
|
||||
private Status status = null;
|
||||
|
||||
/** Current maintenance mode record */
|
||||
private MaintenanceData maintenance = null;
|
||||
|
||||
private SimpleJdbcCall doStartTick;
|
||||
private SimpleJdbcCall doCheckTick;
|
||||
|
||||
private StoredProc fEnterMaintenanceMode;
|
||||
private StoredProc fExtendMaintenanceMode;
|
||||
private StoredProc fExitMaintenanceMode;
|
||||
|
||||
|
||||
@Autowired( required = true )
|
||||
public void setDataSource( DataSource dataSource )
|
||||
{
|
||||
this.dTemplate = new SimpleJdbcTemplate( dataSource );
|
||||
|
||||
this.fEnterMaintenanceMode = new StoredProc( dataSource , "sys" , "enter_maintenance_mode" );
|
||||
this.fEnterMaintenanceMode.addParameter( "admin_id" , Types.INTEGER );
|
||||
this.fEnterMaintenanceMode.addParameter( "reason" , Types.VARCHAR );
|
||||
this.fEnterMaintenanceMode.addParameter( "duration" , Types.INTEGER );
|
||||
this.fEnterMaintenanceMode.addOutput( "success" , Types.BOOLEAN );
|
||||
|
||||
this.fExtendMaintenanceMode = new StoredProc( dataSource , "sys" , "extend_maintenance_mode" );
|
||||
this.fExtendMaintenanceMode.addParameter( "admin_id" , Types.INTEGER );
|
||||
this.fExtendMaintenanceMode.addParameter( "duration" , Types.INTEGER );
|
||||
this.fExtendMaintenanceMode.addOutput( "success" , Types.BOOLEAN );
|
||||
|
||||
this.fExitMaintenanceMode = new StoredProc( dataSource , "sys" , "exit_maintenance_mode" );
|
||||
this.fExitMaintenanceMode.addParameter( "admin_id" , Types.INTEGER );
|
||||
this.fExitMaintenanceMode.addOutput( "success" , Types.BOOLEAN );
|
||||
|
||||
this.doStartTick = new SimpleJdbcCall( dataSource );
|
||||
this.doStartTick.withCatalogName( "sys" ).withFunctionName( "start_tick" );
|
||||
this.doStartTick.withoutProcedureColumnMetaDataAccess( );
|
||||
this.doStartTick.addDeclaredParameter( new SqlOutParameter( "tick_id" , Types.BIGINT ) );
|
||||
|
||||
this.doCheckTick = new SimpleJdbcCall( dataSource );
|
||||
this.doCheckTick.withCatalogName( "sys" ).withFunctionName( "check_stuck_tick" );
|
||||
this.doCheckTick.withoutProcedureColumnMetaDataAccess( );
|
||||
this.doCheckTick.addDeclaredParameter( new SqlOutParameter( "tick_id" , Types.BIGINT ) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the transaction manager interface (DI)
|
||||
*
|
||||
* @param transactionManager
|
||||
* the transaction manager
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setTransactionManager( PlatformTransactionManager transactionManager )
|
||||
{
|
||||
this.tTemplate = new TransactionTemplate( transactionManager );
|
||||
}
|
||||
|
||||
|
||||
private void loadStatus( )
|
||||
{
|
||||
String sql = "SELECT * FROM sys.status";
|
||||
RowMapper< Status > mapper = new RowMapper< Status >( ) {
|
||||
|
||||
@Override
|
||||
public Status mapRow( ResultSet rs , int rowNum )
|
||||
throws SQLException
|
||||
{
|
||||
Status s = new Status( );
|
||||
s.setNextTickIdentifier( rs.getLong( "next_tick" ) );
|
||||
s.setCurrentTick( rs.getLong( "current_tick" ) );
|
||||
s.setLastMsgRecap( rs.getTimestamp( "last_msg_recap" ) );
|
||||
s.setLastAdminRecap( rs.getTimestamp( "last_admin_recap" ) );
|
||||
s.setLastErrorCheck( rs.getTimestamp( "last_error_recap" ) );
|
||||
s.setMaintenanceStart( rs.getTimestamp( "maintenance_start" ) );
|
||||
s.setMaintenanceEnd( rs.getTimestamp( "maintenance_end" ) );
|
||||
s.setMaintenanceReason( rs.getString( "maintenance_text" ) );
|
||||
return s;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.status = this.dTemplate.queryForObject( sql , mapper );
|
||||
|
||||
// Update maintenance status
|
||||
if ( this.status.getMaintenanceReason( ) != null ) {
|
||||
this.maintenance = new MaintenanceData( this.status.getMaintenanceStart( ) , this.status
|
||||
.getMaintenanceEnd( ) , this.status.getMaintenanceReason( ) );
|
||||
} else {
|
||||
this.maintenance = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the system's status.
|
||||
*
|
||||
* <p>
|
||||
* This method attempts to read the system's previous status from the database. If the entry
|
||||
* doesn't exist, it is created. Some of the record's data is then stored locally and the bean
|
||||
* is marked as initialised.
|
||||
*/
|
||||
private void initialise( )
|
||||
{
|
||||
if ( this.status != null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load status
|
||||
this.tTemplate.execute( new TransactionCallbackWithoutResult( ) {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
loadStatus( );
|
||||
}
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in interface */
|
||||
@Override
|
||||
synchronized public MaintenanceData checkMaintenance( )
|
||||
{
|
||||
this.initialise( );
|
||||
return this.maintenance;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in interface */
|
||||
@Override
|
||||
synchronized public void startMaintenance( final int adminId , final String reason , final int duration )
|
||||
throws MaintenanceStatusException
|
||||
{
|
||||
if ( duration <= 0 || reason == null ) {
|
||||
throw new IllegalArgumentException( );
|
||||
}
|
||||
|
||||
boolean s = this.tTemplate.execute( new TransactionCallback< Boolean >( ) {
|
||||
|
||||
@Override
|
||||
public Boolean doInTransaction( TransactionStatus status )
|
||||
{
|
||||
Map< String , Object > m = fEnterMaintenanceMode.execute( adminId , reason , duration );
|
||||
loadStatus( );
|
||||
return (Boolean) m.get( "success" );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
if ( !s ) {
|
||||
throw new MaintenanceStatusException( this.maintenance );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documented in interface */
|
||||
@Override
|
||||
synchronized public void updateMaintenance( final int adminId , final int durationFromNow )
|
||||
throws MaintenanceStatusException
|
||||
{
|
||||
if ( durationFromNow <= 0 ) {
|
||||
throw new IllegalArgumentException( );
|
||||
}
|
||||
|
||||
boolean s = this.tTemplate.execute( new TransactionCallback< Boolean >( ) {
|
||||
|
||||
@Override
|
||||
public Boolean doInTransaction( TransactionStatus status )
|
||||
{
|
||||
Map< String , Object > m = fExtendMaintenanceMode.execute( adminId , durationFromNow );
|
||||
loadStatus( );
|
||||
return (Boolean) m.get( "success" );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
if ( !s ) {
|
||||
throw new MaintenanceStatusException( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documented in interface */
|
||||
@Override
|
||||
synchronized public void endMaintenance( final int adminId )
|
||||
throws MaintenanceStatusException
|
||||
{
|
||||
boolean s = this.tTemplate.execute( new TransactionCallback< Boolean >( ) {
|
||||
|
||||
@Override
|
||||
public Boolean doInTransaction( TransactionStatus status )
|
||||
{
|
||||
Map< String , Object > m = fExitMaintenanceMode.execute( adminId );
|
||||
loadStatus( );
|
||||
return (Boolean) m.get( "success" );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
if ( !s ) {
|
||||
throw new MaintenanceStatusException( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Documented in interface */
|
||||
@Override
|
||||
synchronized public long startTick( )
|
||||
throws TickStatusException , MaintenanceStatusException
|
||||
{
|
||||
Long tid = this.tTemplate.execute( new TransactionCallback< Long >( ) {
|
||||
|
||||
@Override
|
||||
public Long doInTransaction( TransactionStatus status )
|
||||
{
|
||||
Map< String , Object > m = doStartTick.execute( );
|
||||
loadStatus( );
|
||||
return (Long) m.get( "tick_id" );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
if ( tid == null ) {
|
||||
if ( this.maintenance != null ) {
|
||||
throw new MaintenanceStatusException( this.maintenance );
|
||||
} else {
|
||||
throw new TickStatusException( this.status.getCurrentTick( ) );
|
||||
}
|
||||
}
|
||||
|
||||
return tid;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in interface */
|
||||
@Override
|
||||
public Long checkStuckTick( )
|
||||
throws MaintenanceStatusException
|
||||
{
|
||||
Long tid = this.tTemplate.execute( new TransactionCallback< Long >( ) {
|
||||
|
||||
@Override
|
||||
public Long doInTransaction( TransactionStatus status )
|
||||
{
|
||||
Map< String , Object > m = doCheckTick.execute( );
|
||||
loadStatus( );
|
||||
return (Long) m.get( "tick_id" );
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
if ( tid == null && this.maintenance != null ) {
|
||||
throw new MaintenanceStatusException( this.maintenance );
|
||||
}
|
||||
|
||||
return tid;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.deepclone.lw.cmd.admin.logs.LogLevel;
|
||||
import com.deepclone.lw.interfaces.eventlog.Logger;
|
||||
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantsManager;
|
||||
import com.deepclone.lw.interfaces.sys.Ticker;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Task scheduler bean.
|
||||
*
|
||||
* <p>
|
||||
* This class implements Legacy Worlds' task scheduler, used for most repetitive actions such as
|
||||
* updating the game's state, cleaning logs or de-activating accounts.
|
||||
*
|
||||
* <ul>
|
||||
* <li>Tasks with {@link Frequency#HIGH} are executed once every time the period is over.</li>
|
||||
* <li>Tasks with {@link Frequency#MEDIUM} are executed once every 6 periods.</li>
|
||||
* <li>Tasks with {@link Frequency#MINUTE} are executed once every 12 periods.</li>
|
||||
* <li>Tasks with {@link Frequency#LOW} are executed once every 60 periods.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* <em>ticker.interval</em> defaults to 5 seconds.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
public class TickerBean
|
||||
implements Ticker , InitializingBean , DisposableBean
|
||||
{
|
||||
|
||||
/** System logger for the bean */
|
||||
private SystemLogger logger;
|
||||
|
||||
/** Constants manager bean */
|
||||
private ConstantsManager constantsManager;
|
||||
|
||||
/** Main thread for ticker control */
|
||||
private TickerThread mainThread;
|
||||
|
||||
private TickerTaskStatusHandler tickerManager;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the system logger (DI)
|
||||
*
|
||||
* @param logger
|
||||
* reference to the logger bean
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setLogger( Logger logger )
|
||||
{
|
||||
this.logger = logger.getSystemLogger( "Ticker" );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the constants manager (DI)
|
||||
*
|
||||
* @param manager
|
||||
* reference to the constants manager bean
|
||||
*/
|
||||
@Autowired( required = true )
|
||||
public void setConstantsManager( ConstantsManager manager )
|
||||
{
|
||||
this.constantsManager = manager;
|
||||
}
|
||||
|
||||
|
||||
@Autowired( required = true )
|
||||
public void setManager( TickerTaskStatusHandler manager )
|
||||
{
|
||||
this.tickerManager = manager;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the bean.
|
||||
*
|
||||
* <p>
|
||||
* When all dependencies have been set, the bean will register the <em>ticker.interval</em>
|
||||
* system constant. The main control thread will then be created and registered as an user of
|
||||
* the constant (which causes the control thread to schedule itself using the timer).
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet( )
|
||||
{
|
||||
this.logger.log( LogLevel.INFO , "Initialisation" ).flush( );
|
||||
|
||||
// Create thread
|
||||
this.mainThread = new TickerThread( this.logger , this.tickerManager );
|
||||
|
||||
// Register thread as a constants user
|
||||
Set< String > use = new HashSet< String >( );
|
||||
use.add( "ticker.interval" );
|
||||
this.constantsManager.registerUser( this.mainThread , use );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destroys the bean.
|
||||
*
|
||||
* This method will abort the main control thread's execution, and unregister it from the
|
||||
* constants manager.
|
||||
*/
|
||||
@Override
|
||||
public void destroy( )
|
||||
{
|
||||
this.logger.log( LogLevel.INFO , "Destruction" ).flush( );
|
||||
this.mainThread.terminate( );
|
||||
this.constantsManager.unregisterUser( this.mainThread );
|
||||
this.constantsManager = null;
|
||||
}
|
||||
|
||||
|
||||
/* Documented in Ticker interface */
|
||||
@Override
|
||||
public void registerTask( Ticker.Frequency frequency , String name , Runnable task )
|
||||
{
|
||||
this.logger.log( LogLevel.DEBUG , "Registering task " + name + " at frequency " + frequency ).flush( );
|
||||
int id = this.tickerManager.registerTask( name );
|
||||
this.mainThread.registerTask( id , frequency , name , task );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in Ticker interface */
|
||||
@Override
|
||||
public void pause( )
|
||||
throws IllegalStateException
|
||||
{
|
||||
this.mainThread.pause( );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in Ticker interface */
|
||||
@Override
|
||||
public void unpause( )
|
||||
throws IllegalStateException
|
||||
{
|
||||
this.mainThread.unpause( );
|
||||
}
|
||||
|
||||
|
||||
/* Documented in Ticker interface */
|
||||
@Override
|
||||
public boolean isActive( )
|
||||
{
|
||||
return this.mainThread.isActive( );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import com.deepclone.lw.cmd.admin.tick.TickerTaskInfo;
|
||||
import com.deepclone.lw.cmd.admin.tick.TickerTaskStatus;
|
||||
import com.deepclone.lw.interfaces.sys.TickerManager;
|
||||
import com.deepclone.lw.sqld.sys.TickerTaskRecord;
|
||||
import com.deepclone.lw.utils.StoredProc;
|
||||
|
||||
|
||||
|
||||
public class TickerManagerBean
|
||||
implements TickerManager , TickerTaskStatusHandler , InitializingBean
|
||||
{
|
||||
|
||||
private final Map< Integer , TickerTaskRecord > tasks = new HashMap< Integer , TickerTaskRecord >( );
|
||||
private final Map< String , Integer > taskIds = new HashMap< String , Integer >( );
|
||||
private final Set< Integer > registered = new HashSet< Integer >( );
|
||||
|
||||
private TransactionTemplate tTemplate;
|
||||
private SimpleJdbcTemplate dTemplate;
|
||||
|
||||
private final RowMapper< TickerTaskRecord > mTask;
|
||||
|
||||
private StoredProc fRegisterTask;
|
||||
private StoredProc fSetTaskStarted;
|
||||
private StoredProc fSetTaskRunning;
|
||||
private StoredProc fScheduleTask;
|
||||
|
||||
|
||||
public TickerManagerBean( )
|
||||
{
|
||||
this.mTask = new RowMapper< TickerTaskRecord >( ) {
|
||||
@Override
|
||||
public TickerTaskRecord mapRow( ResultSet rs , int rowNum )
|
||||
throws SQLException
|
||||
{
|
||||
TickerTaskRecord task = new TickerTaskRecord( );
|
||||
task.setId( rs.getInt( "id" ) );
|
||||
task.setName( rs.getString( "task_name" ) );
|
||||
task.setStatus( rs.getString( "status" ) );
|
||||
task.setTimestamp( rs.getTimestamp( "auto_start" ) );
|
||||
return task;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Autowired( required = true )
|
||||
public void setTransactionManager( PlatformTransactionManager manager )
|
||||
{
|
||||
this.tTemplate = new TransactionTemplate( manager );
|
||||
}
|
||||
|
||||
|
||||
@Autowired( required = true )
|
||||
public void setDataSource( DataSource dataSource )
|
||||
{
|
||||
this.dTemplate = new SimpleJdbcTemplate( dataSource );
|
||||
|
||||
this.fRegisterTask = new StoredProc( dataSource , "sys" , "register_ticker_task" );
|
||||
this.fRegisterTask.addParameter( "task_name" , Types.VARCHAR );
|
||||
this.fRegisterTask.addOutput( "id" , Types.INTEGER );
|
||||
|
||||
this.fSetTaskStarted = new StoredProc( dataSource , "sys" , "set_task_started" );
|
||||
this.fSetTaskStarted.addParameter( "id" , Types.INTEGER );
|
||||
|
||||
this.fSetTaskRunning = new StoredProc( dataSource , "sys" , "set_task_running" );
|
||||
this.fSetTaskRunning.addParameter( "admin_id" , Types.INTEGER );
|
||||
this.fSetTaskRunning.addParameter( "task_id" , Types.INTEGER );
|
||||
this.fSetTaskRunning.addParameter( "running" , Types.BOOLEAN );
|
||||
|
||||
this.fScheduleTask = new StoredProc( dataSource , "sys" , "schedule_task" );
|
||||
this.fScheduleTask.addParameter( "admin_id" , Types.INTEGER );
|
||||
this.fScheduleTask.addParameter( "task_id" , Types.INTEGER );
|
||||
this.fScheduleTask.addParameter( "time_to_start" , Types.BIGINT );
|
||||
this.fScheduleTask.addOutput( "start_at" , Types.TIMESTAMP );
|
||||
}
|
||||
|
||||
|
||||
private List< TickerTaskRecord > getDBTasks( )
|
||||
{
|
||||
return this.tTemplate.execute( new TransactionCallback< List< TickerTaskRecord > >( ) {
|
||||
@Override
|
||||
public List< TickerTaskRecord > doInTransaction( TransactionStatus status )
|
||||
{
|
||||
return dTemplate.query( "SELECT * FROM sys.ticker" , mTask );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
private void checkAutoStart( final TickerTaskRecord record )
|
||||
{
|
||||
if ( record.getStatus( ).equals( "AUTO" ) ) {
|
||||
Timestamp start = record.getTimestamp( );
|
||||
if ( start.before( new Date( ) ) ) {
|
||||
this.tTemplate.execute( new TransactionCallbackWithoutResult( ) {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
fSetTaskStarted.execute( record.getId( ) );
|
||||
}
|
||||
} );
|
||||
record.setStatus( "RUNNING" );
|
||||
record.setTimestamp( null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setTaskRunning( final int administrator , final TickerTaskRecord record , final boolean running )
|
||||
{
|
||||
this.tTemplate.execute( new TransactionCallbackWithoutResult( ) {
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult( TransactionStatus status )
|
||||
{
|
||||
fSetTaskRunning.execute( administrator , record.getId( ) , running );
|
||||
}
|
||||
} );
|
||||
record.setStatus( running ? "RUNNING" : "STOPPED" );
|
||||
record.setTimestamp( null );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet( )
|
||||
{
|
||||
List< TickerTaskRecord > rTasks = this.getDBTasks( );
|
||||
for ( TickerTaskRecord task : rTasks ) {
|
||||
this.tasks.put( task.getId( ) , task );
|
||||
this.taskIds.put( task.getName( ) , task.getId( ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
synchronized public List< TickerTaskInfo > getTasks( )
|
||||
{
|
||||
List< TickerTaskInfo > result = new LinkedList< TickerTaskInfo >( );
|
||||
long now = new Date( ).getTime( );
|
||||
for ( Integer id : this.registered ) {
|
||||
TickerTaskRecord record = this.tasks.get( id );
|
||||
this.checkAutoStart( record );
|
||||
|
||||
TickerTaskInfo info = new TickerTaskInfo( );
|
||||
info.setId( record.getId( ) );
|
||||
info.setName( record.getName( ) );
|
||||
info.setStatus( TickerTaskStatus.valueOf( record.getStatus( ) ) );
|
||||
if ( info.getStatus( ) == TickerTaskStatus.AUTO ) {
|
||||
info.setStart( record.getTimestamp( ) );
|
||||
info.setTimeToStart( ( record.getTimestamp( ).getTime( ) - now ) / 1000 );
|
||||
}
|
||||
result.add( info );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
synchronized public void startTask( int administrator , int id )
|
||||
{
|
||||
TickerTaskRecord record = this.tasks.get( id );
|
||||
if ( record == null || record.getStatus( ).equals( "RUNNING" ) ) {
|
||||
return;
|
||||
}
|
||||
this.setTaskRunning( administrator , record , true );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
synchronized public void stopTask( int administrator , int id )
|
||||
{
|
||||
TickerTaskRecord record = this.tasks.get( id );
|
||||
if ( record == null || record.getStatus( ).equals( "STOPPED" ) ) {
|
||||
return;
|
||||
}
|
||||
this.setTaskRunning( administrator , record , false );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
synchronized public void setTaskStart( final int administrator , final int id , final long time )
|
||||
{
|
||||
if ( time <= 0 ) {
|
||||
this.startTask( administrator , id );
|
||||
return;
|
||||
}
|
||||
|
||||
TickerTaskRecord record = this.tasks.get( id );
|
||||
if ( record == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
Timestamp startAt = this.tTemplate.execute( new TransactionCallback< Timestamp >( ) {
|
||||
@Override
|
||||
public Timestamp doInTransaction( TransactionStatus status )
|
||||
{
|
||||
return (Timestamp) fScheduleTask.execute( administrator , id , time ).get( "start_at" );
|
||||
}
|
||||
} );
|
||||
record.setStatus( "AUTO" );
|
||||
record.setTimestamp( startAt );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
synchronized public int registerTask( final String name )
|
||||
{
|
||||
Integer eId = this.taskIds.get( name );
|
||||
if ( eId != null ) {
|
||||
this.registered.add( eId );
|
||||
return eId;
|
||||
}
|
||||
|
||||
// Register new task
|
||||
eId = this.tTemplate.execute( new TransactionCallback< Integer >( ) {
|
||||
@Override
|
||||
public Integer doInTransaction( TransactionStatus status )
|
||||
{
|
||||
return (Integer) fRegisterTask.execute( name ).get( "id" );
|
||||
}
|
||||
} );
|
||||
|
||||
TickerTaskRecord record = new TickerTaskRecord( );
|
||||
record.setId( eId );
|
||||
record.setName( name );
|
||||
record.setStatus( "RUNNING" );
|
||||
record.setTimestamp( null );
|
||||
this.tasks.put( eId , record );
|
||||
this.taskIds.put( name , eId );
|
||||
|
||||
return eId;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
synchronized public boolean isTaskRunning( int id )
|
||||
{
|
||||
TickerTaskRecord record = this.tasks.get( id );
|
||||
this.checkAutoStart( record );
|
||||
return ( record.getStatus( ).equals( "RUNNING" ) );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.deepclone.lw.cmd.admin.logs.LogLevel;
|
||||
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Ticker task execution class.
|
||||
*
|
||||
* <p>
|
||||
* This class is responsible for both the execution and the control of ticker tasks.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
class TickerTask
|
||||
extends Thread
|
||||
{
|
||||
/** Possible states of a task execution thread. */
|
||||
private static enum State {
|
||||
|
||||
/** The task thread is being initialised. */
|
||||
STARTING ,
|
||||
|
||||
/** The task thread is waiting for instructions */
|
||||
WAITING ,
|
||||
|
||||
/** The task is currently being executed */
|
||||
RUNNING ,
|
||||
|
||||
/** The task thread is exiting. */
|
||||
EXITING ,
|
||||
|
||||
/** The task thread is finished. */
|
||||
FINISHED
|
||||
}
|
||||
|
||||
/** Reference to the system logger */
|
||||
private final SystemLogger logger;
|
||||
|
||||
/** Name of the task */
|
||||
private final String name;
|
||||
|
||||
/** Weak reference to the task's code */
|
||||
private final WeakReference< Runnable > task;
|
||||
|
||||
/** Lock used to protect the task thread's state */
|
||||
private final Lock lock = new ReentrantLock( );
|
||||
|
||||
/** Condition triggered whenever the thread's state changes */
|
||||
private final Condition stateChanged = this.lock.newCondition( );
|
||||
|
||||
/** The thread's current state */
|
||||
private State state = State.STARTING;
|
||||
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* the task's name
|
||||
* @param task
|
||||
* reference to the task's code
|
||||
* @param logger
|
||||
* system logger instance
|
||||
*/
|
||||
TickerTask( String name , Runnable task , SystemLogger logger )
|
||||
{
|
||||
super( "Ticker thread for '" + name + "'" );
|
||||
this.name = name;
|
||||
this.task = new WeakReference< Runnable >( task );
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts the task execution thread.
|
||||
*
|
||||
* <p>
|
||||
* Starts the thread itself, then waits for the thread's state to change from its default
|
||||
* {@link State#STARTING} value.
|
||||
*/
|
||||
@Override
|
||||
public void start( )
|
||||
{
|
||||
super.start( );
|
||||
this.lock.lock( );
|
||||
try {
|
||||
State state = this.getTaskState( );
|
||||
while ( state == State.STARTING ) {
|
||||
state = this.waitTaskState( );
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the task execution thread's state.
|
||||
*
|
||||
* <p>
|
||||
* This method retrieves the task execution thread's state, locking {@link #lock} to prevent
|
||||
* concurrent access.
|
||||
*
|
||||
* @return the task execution thread's current state.
|
||||
*/
|
||||
private State getTaskState( )
|
||||
{
|
||||
this.lock.lock( );
|
||||
try {
|
||||
return this.state;
|
||||
} finally {
|
||||
this.lock.unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes the task execution thread's state.
|
||||
*
|
||||
* <p>
|
||||
* This method modifies the thread's state, locking {@link #lock} to prevent concurrent access
|
||||
* and triggering {@link #stateChanged} once it's done.
|
||||
*
|
||||
* @param newState
|
||||
* the thread's new state.
|
||||
*/
|
||||
private void setTaskState( State newState )
|
||||
{
|
||||
this.lock.lock( );
|
||||
try {
|
||||
this.state = newState;
|
||||
this.stateChanged.signal( );
|
||||
} finally {
|
||||
this.lock.unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits for a change of state.
|
||||
*
|
||||
* <p>
|
||||
* This method waits for the task's state to change using {@link #stateChanged}, then returns
|
||||
* the new state.
|
||||
*
|
||||
* @return the task execution thread's new state.
|
||||
*/
|
||||
private State waitTaskState( )
|
||||
{
|
||||
this.lock.lock( );
|
||||
try {
|
||||
this.stateChanged.await( );
|
||||
return this.state;
|
||||
} catch ( InterruptedException e ) {
|
||||
return this.state;
|
||||
} finally {
|
||||
this.lock.unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Task execution loop.
|
||||
*
|
||||
* <p>
|
||||
* This method makes sure that the task is executed every time the state is set to
|
||||
* {@link State#RUNNING}, setting it to {@link State#WAITING} when it is inactive.
|
||||
*
|
||||
* <p>
|
||||
* If the thread's state is set to {@link State#EXITING}, or if the task code is no longer
|
||||
* referenced, the method will exit, setting the state to {@link State#FINISHED}.
|
||||
*/
|
||||
@Override
|
||||
public void run( )
|
||||
{
|
||||
while ( true ) {
|
||||
State state;
|
||||
|
||||
this.lock.lock( );
|
||||
try {
|
||||
if ( this.getTaskState( ) == State.EXITING ) {
|
||||
break;
|
||||
}
|
||||
this.setTaskState( State.WAITING );
|
||||
state = this.waitTaskState( );
|
||||
} finally {
|
||||
this.lock.unlock( );
|
||||
}
|
||||
|
||||
if ( state == State.EXITING || !this.runTask( ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setTaskState( State.FINISHED );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Runs the task's code.
|
||||
*
|
||||
* <p>
|
||||
* This method runs the task's code, if it is still referenced. All exceptions will be caught
|
||||
* and logged, but they will not affect the main loop.
|
||||
*
|
||||
* @return <code>true</code> if the task was still referenced, <code>false</code> if it wasn't.
|
||||
*/
|
||||
private boolean runTask( )
|
||||
{
|
||||
Runnable task = this.task.get( );
|
||||
if ( task == null ) {
|
||||
this.logger.log( LogLevel.INFO , "task '" + this.name + "' is no longer referenced, exiting" )
|
||||
.flush( );
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
this.logger.log( LogLevel.TRACE , "task '" + this.name + "' started" ).flush( );
|
||||
task.run( );
|
||||
this.logger.log( LogLevel.TRACE , "task '" + this.name + "' ended" ).flush( );
|
||||
} catch ( Throwable t ) {
|
||||
this.logger.log( LogLevel.ERROR , "task '" + this.name + "' failed due to exception" , t ).flush( );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Triggers the task's execution.
|
||||
*
|
||||
* <p>
|
||||
* This method attempts to trigger the task's execution. It will not do anything if the task
|
||||
* thread is exiting or finished, but if the task is still running, a warning will be added to
|
||||
* the log.
|
||||
*/
|
||||
void startTask( )
|
||||
{
|
||||
this.lock.lock( );
|
||||
try {
|
||||
State state = this.getTaskState( );
|
||||
if ( state == State.EXITING || state == State.FINISHED ) {
|
||||
return;
|
||||
} else if ( state == State.RUNNING ) {
|
||||
this.logger.log( LogLevel.WARNING , "task '" + this.name + "' didn't manage to run in time" )
|
||||
.flush( );
|
||||
return;
|
||||
}
|
||||
this.setTaskState( State.RUNNING );
|
||||
} finally {
|
||||
this.lock.unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Terminates the task execution thread.
|
||||
*
|
||||
* <p>
|
||||
* This method shuts down the task execution thread, waiting until it has completed to return.
|
||||
*/
|
||||
void terminate( )
|
||||
{
|
||||
this.lock.lock( );
|
||||
try {
|
||||
State state = this.getTaskState( );
|
||||
while ( state != State.FINISHED ) {
|
||||
this.setTaskState( State.EXITING );
|
||||
state = this.waitTaskState( );
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock( );
|
||||
}
|
||||
this.logger.log( LogLevel.DEBUG , "task '" + this.name + "' terminated" ).flush( );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Waits until the task is done running.
|
||||
*
|
||||
* <p>
|
||||
* This method waits until the task is no longer running. If the task is not running when the
|
||||
* method is called, it will return immediately.
|
||||
*/
|
||||
void waitForTask( )
|
||||
{
|
||||
this.lock.lock( );
|
||||
try {
|
||||
State state = this.getTaskState( );
|
||||
while ( state == State.RUNNING ) {
|
||||
state = this.waitTaskState( );
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the task execution thread is no longer running,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
boolean isFinished( )
|
||||
{
|
||||
return ( this.getTaskState( ) == State.FINISHED );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
public interface TickerTaskStatusHandler
|
||||
{
|
||||
|
||||
public int registerTask( String name );
|
||||
|
||||
|
||||
public boolean isTaskRunning( int id );
|
||||
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
package com.deepclone.lw.beans.sys;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
|
||||
import com.deepclone.lw.interfaces.sys.ConstantsUser;
|
||||
import com.deepclone.lw.interfaces.sys.Ticker;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Main ticker control thread.
|
||||
*
|
||||
* <p>
|
||||
* This class implements the ticker bean's main control thread, executed through a Java
|
||||
* {@link Timer} instance. It supports changing the timer's period when the <em>ticker.interval</em>
|
||||
* constant is modified, and includes the code that pauses or reactivates the ticker.
|
||||
*
|
||||
* @author tseeker
|
||||
*/
|
||||
class TickerThread
|
||||
implements ConstantsUser
|
||||
{
|
||||
private static class TaskStat
|
||||
{
|
||||
public final int id;
|
||||
public final TickerTask task;
|
||||
public final int modulo;
|
||||
public int counter = 0;
|
||||
|
||||
|
||||
public TaskStat( int id , TickerTask task , int modulo )
|
||||
{
|
||||
this.id = id;
|
||||
this.task = task;
|
||||
this.task.start( );
|
||||
this.modulo = modulo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Timer timer;
|
||||
|
||||
private long interval = 0;
|
||||
|
||||
private boolean paused = false;
|
||||
|
||||
private int taskIndex = 0;
|
||||
|
||||
private int nTasks = 0;
|
||||
|
||||
private TaskStat[] tasks;
|
||||
|
||||
private TickerTaskStatusHandler handler;
|
||||
|
||||
private SystemLogger logger;
|
||||
|
||||
|
||||
TickerThread( SystemLogger logger , TickerTaskStatusHandler handler )
|
||||
{
|
||||
this.timer = null;
|
||||
this.tasks = new TaskStat[ 5 ];
|
||||
this.handler = handler;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
||||
synchronized private void runTasks( )
|
||||
{
|
||||
if ( this.paused ) {
|
||||
return;
|
||||
}
|
||||
this.taskIndex = ( this.taskIndex + 1 ) % this.tasks.length;
|
||||
|
||||
TaskStat toExecute = this.tasks[ this.taskIndex ];
|
||||
if ( toExecute == null || !this.handler.isTaskRunning( toExecute.id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
toExecute.counter = ( toExecute.counter + 1 ) % toExecute.modulo;
|
||||
if ( toExecute.counter != 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( toExecute.task.isFinished( ) ) {
|
||||
this.tasks[ this.taskIndex ] = null;
|
||||
this.nTasks--;
|
||||
return;
|
||||
}
|
||||
|
||||
toExecute.task.startTask( );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the ticker's frequency.
|
||||
*
|
||||
* <p>
|
||||
* When the <em>ticker.interval</em> constant is changed, this method will cancel the current
|
||||
* scheduling and re-start it using the new interval.
|
||||
*/
|
||||
@Override
|
||||
synchronized public void setConstants( boolean initial , Map< String , Double > values )
|
||||
{
|
||||
this.interval = Math.round( values.get( "ticker.interval" ) );
|
||||
this.reschedule( );
|
||||
}
|
||||
|
||||
|
||||
private void reschedule( )
|
||||
{
|
||||
if ( this.interval == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this.timer != null ) {
|
||||
this.timer.cancel( );
|
||||
}
|
||||
|
||||
TimerTask task = new TimerTask( ) {
|
||||
@Override
|
||||
public void run( )
|
||||
{
|
||||
runTasks( );
|
||||
}
|
||||
};
|
||||
|
||||
this.timer = new Timer( "Main ticker thread" );
|
||||
this.timer.scheduleAtFixedRate( task , 1 , this.interval / this.tasks.length );
|
||||
}
|
||||
|
||||
|
||||
synchronized void registerTask( int id , Ticker.Frequency frequency , String name , Runnable task )
|
||||
{
|
||||
if ( this.nTasks == this.tasks.length ) {
|
||||
TaskStat[] nState = new TaskStat[ this.tasks.length * 2 ];
|
||||
for ( int i = 0 ; i < this.tasks.length ; i++ ) {
|
||||
nState[ i * 2 ] = this.tasks[ i ];
|
||||
}
|
||||
this.taskIndex *= 2;
|
||||
this.tasks = nState;
|
||||
this.reschedule( );
|
||||
}
|
||||
|
||||
int modulo;
|
||||
switch ( frequency ) {
|
||||
case HIGH:
|
||||
modulo = 1;
|
||||
break;
|
||||
case LOW:
|
||||
modulo = 30;
|
||||
break;
|
||||
case MEDIUM:
|
||||
modulo = 6;
|
||||
break;
|
||||
case MINUTE:
|
||||
modulo = 12;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException( "Invalid timer frequency " + frequency );
|
||||
}
|
||||
|
||||
for ( int i = 0 ; i < this.tasks.length ; i++ ) {
|
||||
if ( this.tasks[ i ] == null ) {
|
||||
this.tasks[ i ] = new TaskStat( id , new TickerTask( name , task , this.logger ) , modulo );
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.nTasks ++;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pauses the ticker.
|
||||
*
|
||||
* <p>
|
||||
* This method implements the bean's pause method. If the ticker is not already paused, it will
|
||||
* set the {@link #paused} flag, then wait for all running tasks to complete.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* if the ticker is already paused.
|
||||
*/
|
||||
synchronized void pause( )
|
||||
throws IllegalStateException
|
||||
{
|
||||
if ( this.paused ) {
|
||||
throw new IllegalStateException( "already paused" );
|
||||
}
|
||||
this.paused = true;
|
||||
for ( int i = 0 ; i < this.tasks.length ; i++ ) {
|
||||
TaskStat ts = this.tasks[ i ];
|
||||
if ( ts == null ) {
|
||||
continue;
|
||||
}
|
||||
if ( ts.task.isFinished( ) ) {
|
||||
this.tasks[ i ] = null;
|
||||
this.nTasks--;
|
||||
continue;
|
||||
}
|
||||
ts.task.waitForTask( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restarts the ticker after it's been paused.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* if the ticker was not paused.
|
||||
*/
|
||||
synchronized void unpause( )
|
||||
throws IllegalStateException
|
||||
{
|
||||
if ( !this.paused ) {
|
||||
throw new IllegalStateException( "not paused" );
|
||||
}
|
||||
this.paused = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Terminates all tasks in all task sets, then stops the timer.
|
||||
*/
|
||||
synchronized void terminate( )
|
||||
{
|
||||
for ( TaskStat ts : this.tasks ) {
|
||||
if ( ts == null ) {
|
||||
continue;
|
||||
}
|
||||
ts.task.terminate( );
|
||||
}
|
||||
this.nTasks = 0;
|
||||
this.tasks = new TaskStat[ 5 ];
|
||||
if ( this.timer != null ) {
|
||||
this.timer.cancel( );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return <code>true</code> the ticker is currently running or <code>false</code> if it has
|
||||
* been paused.
|
||||
*/
|
||||
synchronized boolean isActive( )
|
||||
{
|
||||
return !this.paused;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<import resource="system/constants-manager-bean.xml" />
|
||||
<import resource="system/constants-registrar-bean.xml" />
|
||||
<import resource="system/ticker-bean.xml" />
|
||||
<import resource="system/session-manager-bean.xml" />
|
||||
<import resource="system/system-status-bean.xml" />
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<bean id="constantsManager" class="com.deepclone.lw.beans.sys.ConstantsManagerBean" />
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<bean id="constantsRegistrar" class="com.deepclone.lw.beans.sys.ConstantsRegistrarBean" />
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<bean id="authChallengeGenerator" class="com.deepclone.lw.utils.RandomStringGenerator">
|
||||
<qualifier value="authChallenges" />
|
||||
<property name="length" value="100" />
|
||||
<property name="characterSet" value="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" />
|
||||
</bean>
|
||||
|
||||
<bean id="sessionIDGenerator" class="com.deepclone.lw.utils.RandomStringGenerator">
|
||||
<qualifier value="sessionIdentifiers" />
|
||||
<property name="length" value="50" />
|
||||
<property name="characterSet" value="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" />
|
||||
</bean>
|
||||
|
||||
<bean id="sessionManager" class="com.deepclone.lw.beans.sys.SessionManagerBean" />
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<bean id="systemStatus" class="com.deepclone.lw.beans.sys.SystemStatusBean" />
|
||||
|
||||
</beans>
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<bean id="tickerManager" class="com.deepclone.lw.beans.sys.TickerManagerBean" />
|
||||
<bean id="ticker" class="com.deepclone.lw.beans.sys.TickerBean" />
|
||||
|
||||
</beans>
|
0
legacyworlds-server-beans-system/src/test/java/.empty
Normal file
0
legacyworlds-server-beans-system/src/test/java/.empty
Normal file
Reference in a new issue