package com.pugh.sockso.web;
import com.pugh.sockso.Constants;
import com.pugh.sockso.Properties;
import com.pugh.sockso.db.Database;
import com.pugh.sockso.resources.Locale;
import com.pugh.sockso.resources.LocaleFactory;
import com.pugh.sockso.resources.Resources;
import com.pugh.sockso.web.action.Errorer;
import com.pugh.sockso.web.action.WebAction;
import com.pugh.sockso.web.log.DbRequestLogger;
import com.pugh.sockso.web.log.RequestLogger;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.net.Socket;
import java.net.SocketException;
import org.apache.log4j.Logger;
import com.google.inject.Inject;
public class ServerThread extends Thread {
private static final Logger log = Logger.getLogger( ServerThread.class );
private final Server sv;
private final Database db;
private final Properties p;
private final Resources r;
private final Dispatcher dispatcher;
private final LocaleFactory localeFactory;
private Socket client;
/**
* Creates a new instance of ServerThread
*
* @param server the server this thread is attached to
* @param db the database connection
* @param p app properties
* @param r app resources
*
*/
@Inject
public ServerThread( final Server server, final Database db, final Properties p,
final Resources r, final Dispatcher dispatcher, final LocaleFactory localeFactory ) {
this.sv = server;
this.db = db;
this.p = p;
this.r = r;
this.dispatcher = dispatcher;
this.localeFactory = localeFactory;
}
/**
* Sets the client socket to use to communicate with the caller
*
* @param client
*
*/
public void setClientSocket( final Socket client ) {
this.client = client;
}
/**
* the service, handles the request
*
*/
@Override
public void run() {
Response res = null;
Request req = null;
User user = null;
try {
req = new HttpRequest( sv );
req.process( new BufferedInputStream(client.getInputStream()) );
Locale locale = localeFactory.getLocale( req.getPreferredLangCode() );
final WebAction action = dispatcher.getAction( req );
action.setRequest( req );
action.setLocale( locale );
if ( action.requiresSession() ) {
final Session session = new Session( db, req, null );
user = session.getCurrentUser();
}
action.setUser( user );
res = new HttpResponse(
new BufferedOutputStream(client.getOutputStream()),
db, p, locale, user,
req.getHeader("Accept-Encoding").contains("gzip")
// @TODO fix safari gzip bug
&& !req.getHeader("User-Agent").contains("Safari")
);
action.setResponse( res );
process( action, user, req, locale, res );
}
// client has probably disconnected, ignore
catch ( final SocketException e ) {}
// ignore unknown IE6 behaviour
catch ( final EmptyRequestException e ) {}
// "handled" exception from Sockso
catch ( final BadRequestException e ) {
showException( e, req, res, false );
}
// something bad like IO, SQL, etc...
catch ( final Exception e ) {
// create our own (exception for the template) but clone the stack trace...
final BadRequestException exception = new BadRequestException( e.getMessage(), 500 );
exception.setStackTrace( e.getStackTrace() );
showException( exception, req, res, true );
}
finally {
// we're done with the request so we can close the
// connection to the client now.
try { client.close(); }
catch ( Exception e ) {}
}
sv.requestComplete( this );
}
/**
* Process a request and generate a response
*
* @param action
* @param user
* @param req
* @param locale
* @param res
*
* @throws Exception
*
*/
protected void process( final WebAction action, final User user, final Request req, final Locale locale, final Response res ) throws Exception {
if ( p.get(Constants.WWW_LOG_REQUESTS_ENABLED).equals(Properties.YES) ) {
final RequestLogger logger = new DbRequestLogger( db );
logger.log( user, client.getInetAddress().getHostAddress(),
req.getResource(), req.getHeader("User-Agent"),
req.getHeader("Referer"), req.getHeader("Cookie") );
}
if ( action == null ) {
throw new BadRequestException(locale.getString("www.error.unknownRequest"), 400);
}
if ( loginRequired(user,action) ) {
res.redirect( p.getUrl("/user/login"));
}
else {
action.handleRequest();
}
}
/**
* Indicates if the user needs to log in before running this action
*
* @param user
* @param action
*
* @return
*
*/
protected boolean loginRequired( final User user, final WebAction action ) {
return p.get( Constants.WWW_USERS_REQUIRE_LOGIN ).equals( p.YES )
&& user == null
&& action.requiresLogin();
}
/**
* Shows an error, and sends the status code in the response
*
* @param e
* @param req
* @param res
* @param showStackTrace
*
*/
private void showException( final BadRequestException e, final Request req, final Response res, final boolean showStackTrace ) {
log.error( e.getMessage() );
if ( showStackTrace ) {
e.printStackTrace();
}
final Errorer err = new Errorer( e, showStackTrace );
err.setRequest( req );
err.setResponse( res );
try { err.handleRequest(); }
catch ( final Exception e2 ) { /* an error showing the error message, ummm.. */ }
}
/**
* tries to shut down the thread cleanly
*
*/
public void shutdown() {
log.info( "Shutting Down" );
try { finalize(); }
catch ( Throwable t ) {}
}
}