blob: 210d2c10c7619871a15c34e9a289eb3a410a1119 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2013 Innoopract Informationssysteme GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
******************************************************************************/
package org.eclipse.rap.rwt.internal.service;
import java.io.IOException;
import java.text.MessageFormat;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.WebClient;
import org.eclipse.rap.rwt.internal.RWTMessages;
import org.eclipse.rap.rwt.internal.json.JsonValue;
import org.eclipse.rap.rwt.internal.lifecycle.LifeCycle;
import org.eclipse.rap.rwt.internal.lifecycle.LifeCycleFactory;
import org.eclipse.rap.rwt.internal.lifecycle.RequestCounter;
import org.eclipse.rap.rwt.internal.protocol.ClientMessage;
import org.eclipse.rap.rwt.internal.protocol.ClientMessageConst;
import org.eclipse.rap.rwt.internal.protocol.ProtocolMessageWriter;
import org.eclipse.rap.rwt.internal.protocol.ProtocolUtil;
import org.eclipse.rap.rwt.internal.remote.RemoteObjectLifeCycleAdapter;
import org.eclipse.rap.rwt.internal.util.HTTP;
import org.eclipse.rap.rwt.service.ServiceHandler;
import org.eclipse.rap.rwt.service.UISession;
public class LifeCycleServiceHandler implements ServiceHandler {
private static final String PROP_ERROR = "error";
private static final String PROP_MESSAGE = "message";
private static final String SESSION_STARTED
= LifeCycleServiceHandler.class.getName() + "#isSessionStarted";
private final LifeCycleFactory lifeCycleFactory;
private final StartupPage startupPage;
public LifeCycleServiceHandler( LifeCycleFactory lifeCycleFactory, StartupPage startupPage ) {
this.lifeCycleFactory = lifeCycleFactory;
this.startupPage = startupPage;
}
public void service( HttpServletRequest request, HttpServletResponse response )
throws IOException
{
// Do not use session store itself as a lock
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=372946
UISessionImpl uiSession = ( UISessionImpl )ContextProvider.getUISession();
synchronized( uiSession.getRequestLock() ) {
synchronizedService( request, response );
}
}
void synchronizedService( HttpServletRequest request, HttpServletResponse response )
throws IOException
{
if( HTTP.METHOD_POST.equals( request.getMethod() ) ) {
try {
handlePostRequest( request, response );
} finally {
if( !isSessionShutdown() ) {
markSessionStarted();
}
}
} else {
handleGetRequest( request, response );
}
}
private void handleGetRequest( ServletRequest request, HttpServletResponse response )
throws IOException
{
if( RWT.getClient() instanceof WebClient ) {
startupPage.send( response );
} else {
StartupJson.send( response );
}
}
private void handlePostRequest( HttpServletRequest request, HttpServletResponse response )
throws IOException
{
setJsonResponseHeaders( response );
if( isSessionShutdown() ) {
handleSessionShutdown( request );
} else if( isSessionTimeout() ) {
handleSessionTimeout( response );
} else if( !isRequestCounterValid() ) {
handleInvalidRequestCounter( response );
} else {
if( isSessionRestart() ) {
reinitializeUISession( request );
reinitializeServiceStore();
}
UrlParameters.merge();
runLifeCycle();
}
writeProtocolMessage( response );
}
private void runLifeCycle() throws IOException {
if( hasInitializeParameter() ) {
// TODO [tb] : This is usually done in DisplayLCA#readData, but the ReadData
// phase is omitted in the first POST request. Since RemoteObjects may already be registered
// at this point, this workaround is currently required. We should find a solution that
// does not require RemoteObjectLifeCycleAdapter.readData to be called in different places.
RemoteObjectLifeCycleAdapter.readData();
}
LifeCycle lifeCycle = lifeCycleFactory.getLifeCycle();
lifeCycle.execute();
}
//////////////////
// helping methods
private static boolean isRequestCounterValid() {
return hasInitializeParameter() || RequestCounter.getInstance().isValid();
}
private static void handleSessionShutdown( HttpServletRequest request ) {
UISessionImpl uiSession = ( UISessionImpl )ContextProvider.getUISession();
uiSession.shutdown();
}
private static void handleInvalidRequestCounter( HttpServletResponse response ) {
int statusCode = HttpServletResponse.SC_PRECONDITION_FAILED;
String errorType = "invalid request counter";
String errorMessage = RWTMessages.getMessage( "RWT_MultipleInstancesErrorMessage" );
renderError( response, statusCode, errorType, formatMessage( errorMessage ) );
}
private static void handleSessionTimeout( HttpServletResponse response ) {
int statusCode = HttpServletResponse.SC_FORBIDDEN;
String errorType = "session timeout";
String errorMessage = RWTMessages.getMessage( "RWT_SessionTimeoutErrorMessage" );
renderError( response, statusCode, errorType, formatMessage( errorMessage ) );
}
private static String formatMessage( String message ) {
Object[] arguments = new Object[]{ "<a {HREF_URL}>", "</a>" };
return MessageFormat.format( message, arguments );
}
private static void renderError( HttpServletResponse response,
int statusCode,
String errorType,
String errorMessage )
{
response.setStatus( statusCode );
ProtocolMessageWriter writer = ContextProvider.getProtocolWriter();
writer.appendHead( PROP_ERROR, JsonValue.valueOf( errorType ) );
writer.appendHead( PROP_MESSAGE, JsonValue.valueOf( errorMessage ) );
}
private static void reinitializeUISession( HttpServletRequest request ) {
ServiceContext serviceContext = ContextProvider.getContext();
UISessionImpl uiSession = ( UISessionImpl )ContextProvider.getUISession();
uiSession.shutdown();
UISessionBuilder builder = new UISessionBuilder( serviceContext );
uiSession = builder.buildUISession();
serviceContext.setUISession( uiSession );
}
private static void reinitializeServiceStore() {
ClientMessage clientMessage = ProtocolUtil.getClientMessage();
ServiceStore serviceStore = ContextProvider.getServiceStore();
serviceStore.clear();
ProtocolUtil.setClientMessage( clientMessage );
}
/*
* Session restart: we're in the same HttpSession and start over (e.g. by pressing F5)
*/
private static boolean isSessionRestart() {
return isSessionStarted() && hasInitializeParameter();
}
private static boolean isSessionTimeout() {
// Session is not initialized because we got a new HTTPSession
return !isSessionStarted() && !hasInitializeParameter();
}
static void markSessionStarted() {
UISession uiSession = ContextProvider.getUISession();
uiSession.setAttribute( SESSION_STARTED, Boolean.TRUE );
}
private static boolean isSessionStarted() {
UISession uiSession = ContextProvider.getUISession();
return Boolean.TRUE.equals( uiSession.getAttribute( SESSION_STARTED ) );
}
private static boolean isSessionShutdown() {
return "true".equals( ProtocolUtil.readHeadPropertyValue( ClientMessageConst.RWT_SHUTDOWN ) );
}
private static boolean hasInitializeParameter() {
return "true".equals( ProtocolUtil.readHeadPropertyValue( ClientMessageConst.RWT_INITIALIZE ) );
}
private static void setJsonResponseHeaders( ServletResponse response ) {
response.setContentType( HTTP.CONTENT_TYPE_JSON );
response.setCharacterEncoding( HTTP.CHARSET_UTF_8 );
}
private static void writeProtocolMessage( ServletResponse response ) throws IOException {
ProtocolMessageWriter protocolWriter = ContextProvider.getProtocolWriter();
String message = protocolWriter.createMessage();
response.getWriter().write( message );
}
}