package fr.lteconsulting.hexa.server.spring;
import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.Properties;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import fr.lteconsulting.hexa.server.data.UserDTO;
import fr.lteconsulting.hexa.server.data.UserSecurityTokenDTO;
import fr.lteconsulting.hexa.server.database.DatabaseContext;
import fr.lteconsulting.hexa.server.database.DatabaseContextFactory;
import fr.lteconsulting.hexa.server.tools.LoggerFactory;
/**
* HexaSpring is a singleton instance helping to manage
* an application. It has a db factory and handle
* some servlet processing.
*
* @author Arnaud
*
*/
public class HexaSpring
{
private static final Logger log = LoggerFactory.getLogger();
private static HexaSpring instance = null;
private static DatabaseContextFactory databaseContextFactory;
public static final String LOGGED_USER_ID = "LOGGED_USER_ID";
public static final String USER_TOKEN_URL_PARAM_NAME = "security";
/**
* Retrieves the HexaSpring instance
*
* @return HexaSpring instance
*/
public static HexaSpring hexa()
{
if( instance == null )
instance = new HexaSpring();
return instance;
}
public interface InitPropertyProvider
{
String getRootDataDir();
String getDatabaseUri();
String getServerRootUrl();
String getAdministratorEmeail();
}
public final void onContextInitialized( ServletContextEvent servletContextEvent )
{
ServletContext c = servletContextEvent.getServletContext();
init( c );
}
//private String configurationDirectory;
private String rootDataDir;
private String databaseUri;
private String serverRootUrl;
private String administratorEmail;
// configuration
/**
* Gets the application's root directory.
*
* @return
*/
public String rootDataDirectory()
{
return rootDataDir;
}
public String administratorEmail()
{
return administratorEmail;
}
public String serverRootUrl()
{
return serverRootUrl;
}
//
public void onBeginServletRequestProcessing( HttpServletRequest request, HttpServletResponse response )
{
// store session information
HexaThreadInfo info = HexaThreadInfo.get();
info.request = request;
// auto-login :
String tokenId = request.getParameter( USER_TOKEN_URL_PARAM_NAME );
if( tokenId != null )
{
UserSecurityTokenDTO token = HexaSpring.hexa().db().qpath.queryOneDTO( UserSecurityTokenDTO.class, "user_security_tokens [id='" + tokenId + "']" );
if( token != null && token.validUntil.compareTo( new Date() ) >= 0 )
{
UserDTO user = HexaSpring.hexa().db().qpath.queryOneDTO( UserDTO.class, "users [id=" + token.userId + "]" );
if( user != null )
userIn( user );
}
}
}
public void onEndServletRequestProcessing()
{
cleanThread();
}
public void runInBackground( final Runnable runnable )
{
Thread thread = new Thread( new Runnable()
{
@Override
public void run()
{
log.info( "Starting a background thread..." );
runnable.run();
cleanThread();
log.info( "Background thread stopped" );
}
} );
thread.start();
}
public interface TransactionManagedAction<T>
{
T execute( DatabaseContext ctx );
}
public <T> T manageTransaction( TransactionManagedAction<T> action )
{
return manageTransaction( db(), action );
}
// do a transaction management : prepare tx, and watch for any exception,
// then rollback it.
// it everything goes fine, commit
public <T> T manageTransaction( DatabaseContext ctx, TransactionManagedAction<T> action )
{
ctx.db.startTransaction();
try
{
T result = action.execute( ctx );
ctx.db.commit();
return result;
}
catch( Exception exception )
{
ctx.db.rollback();
throw new ManagedTransactionException( "Exception during managed transaction, see cause for details", exception );
}
}
private void cleanThread()
{
HexaThreadInfo info = HexaThreadInfo.getIfPresent();
if( info == null )
return;
if( info.request != null )
info.request = null;
// release current thread database context
if( info.databaseContext != null )
{
databaseContextFactory.releaseDatabaseContext( info.databaseContext );
info.databaseContext = null;
}
}
// handles the database connection creation
public synchronized DatabaseContextFactory dbFactory( String databaseUri )
{
DatabaseContextFactory factory = new DatabaseContextFactory();
if( ! factory.init( databaseUri ) )
{
log.error( "Cannot initialize database connection pool, it won't be available to the program !" );
return null;
}
return factory;
}
public DatabaseContext db()
{
HexaThreadInfo info = HexaThreadInfo.get();
if( info.databaseContext != null )
return info.databaseContext;
info.databaseContext = databaseContextFactory.requestDatabaseContext();
assert info.databaseContext != null;
return info.databaseContext;
}
// Store HTTP session thread wide
public HttpSession httpSession()
{
return HexaThreadInfo.get().request.getSession();
}
public HttpServletRequest httpRequest()
{
return HexaThreadInfo.get().request;
}
// manage per http session logged user
public void userIn( UserDTO user )
{
// store session wide
httpSession().setAttribute( LOGGED_USER_ID, user );
}
public void userOut()
{
httpSession().invalidate();
}
public UserDTO user()
{
return (UserDTO) httpSession().getAttribute( LOGGED_USER_ID );
}
private void init( ServletContext c )
{
log.info( "Initialisation..." );
log.info( " ... Properties" );
String hexaSpringProperties = c.getInitParameter( "hexa.spring.properties" );
String hexaSpringPropertiesProvider = c.getInitParameter( "hexa.spring.properties-provider" );
String hexaSpringApplicationBootstrap = c.getInitParameter( "hexa.spring.application-bootstrap" );
if( hexaSpringProperties != null )
initProperties( hexaSpringProperties );
else
initPropertiesByProvider( hexaSpringPropertiesProvider );
if( databaseUri != null )
{
log.info( " ... DatabaseContext pool" );
databaseContextFactory = dbFactory( databaseUri );
}
else
{
log.info( " ... Skiping default DatabaseContext pool because not used" );
}
initApplicationBootstrap( hexaSpringApplicationBootstrap, c );
log.info( "Initialisation Ok." );
}
private void initProperties( String hexaSpringProperties )
{
if( hexaSpringProperties == null )
{
log.warn( "No HexaSpring properties given in parameter, aborting configuration." );
logConfig();
return;
}
log.info( " ... Configuring through file " + hexaSpringProperties );
//configurationDirectory = System.getProperty( "jboss.server.config.dir" );
//String filename = "photo-config.properties";
//File f = new File( configurationDirectory, filename );
File f = new File( hexaSpringProperties );
Properties p = new Properties();
try
{
p.load( new FileInputStream( f ) );
}
catch( Exception e )
{
log.info( " ... Failed ! " + e.getMessage() );
e.printStackTrace();
}
rootDataDir = p.getProperty( "root_data_dir" );
databaseUri = p.getProperty( "database_uri" );
serverRootUrl = p.getProperty( "server_root_url" );
administratorEmail = p.getProperty( "administrator_email" );
logConfig();
}
private void initPropertiesByProvider( String hexaSpringPropertiesProvider )
{
try
{
if( hexaSpringPropertiesProvider == null )
{
log.warn( "No HexaSpring property provider given in parameter, aborting configuration." );
logConfig();
return;
}
log.info( " ... Configuring through class " + hexaSpringPropertiesProvider );
Class<?> cls = Class.forName( hexaSpringPropertiesProvider );
Object provider = cls.newInstance();
if( provider==null || ! (provider instanceof InitPropertyProvider) )
return;
InitPropertyProvider p = (InitPropertyProvider) provider;
rootDataDir = p.getRootDataDir();
databaseUri = p.getDatabaseUri();
serverRootUrl = p.getServerRootUrl();
administratorEmail = p.getAdministratorEmeail();
logConfig();
}
catch( ClassNotFoundException e )
{
log.info( " ... Failed ! " + e.getMessage() );
e.printStackTrace();
}
catch( InstantiationException e )
{
log.info( " ... Failed ! " + e.getMessage() );
e.printStackTrace();
}
catch( IllegalAccessException e )
{
log.info( " ... Failed ! " + e.getMessage() );
e.printStackTrace();
}
}
private void initApplicationBootstrap( String applicationBootstrapClassName, ServletContext c )
{
if( applicationBootstrapClassName == null )
{
log.warn( "No application bootstrap class, skipping." );
return;
}
try
{
Class<?> applicationBootstrapClass = Class.forName( applicationBootstrapClassName );
Object object = applicationBootstrapClass.newInstance();
if( ! ( object instanceof IApplicationBootstrap ) )
{
log.error( "Application Bootstrap object is not implementing " + IApplicationBootstrap.class.getName() + ", its class is " + object.getClass().getName() );
return;
}
// proceed the application initialization
log.info( "Initializing application through class " + applicationBootstrapClassName );
((IApplicationBootstrap)object).onStartup( c );
}
catch( ClassNotFoundException e )
{
log.error( "Cannot load application bootstrap class" + applicationBootstrapClassName, e );
}
catch( InstantiationException e )
{
log.error( "Cannot instantiate application bootstrap class" + applicationBootstrapClassName, e );
}
catch( IllegalAccessException e )
{
log.error( "Cannot access application bootstrap class" + applicationBootstrapClassName, e );
}
}
private void logConfig()
{
log.info( " ... root_data_dir: " + rootDataDir );
log.info( " ... database_uri: " + databaseUri );
log.info( " ... server_root_url: " + serverRootUrl );
log.info( " ... administrator_email: " + administratorEmail );
}
}