package fr.lteconsulting.hexa.client.comm;
import java.util.ArrayList;
import java.util.HashMap;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
/*
* class CachedServerComm
*
* Handles the process of caching service calls results
* It mainly replicate ServerComm interface but with
* added parameters to manage cached data
*
* It also ensures that answers are given to the clients
* in the order they were asked.
*/
public class CachedServerComm implements XRPCRequest, AcceptsRPCRequests
{
RPCProxy srv = new RPCProxy();
public void Init( String baseUrl, XRPCProxy serverCommMessageCb )
{
srv.Init( baseUrl, serverCommMessageCb );
}
public RPCProxy getInternalServerComm()
{
return srv;
}
private final ArrayList<PendingRequestInfo> pendingRequests = new ArrayList<PendingRequestInfo>();
private final HashMap<String, ResponseJSO> cache = new HashMap<String, ResponseJSO>();
// requests as received by the sendRequest method
private ArrayList<RequestCallInfo> requestStack = new ArrayList<RequestCallInfo>();
private boolean fCallbackingScheduled = false;
class PendingRequestInfo
{
boolean fStoreResultInCache;
String requestKey;
ArrayList<RequestCallInfo> subscriptions = new ArrayList<RequestCallInfo>();
public PendingRequestInfo( boolean fStoreResultInCache, RequestCallInfo requestCallInfo )
{
this.fStoreResultInCache = fStoreResultInCache;
requestKey = requestCallInfo.request.getUniqueKey();
addSubscription( requestCallInfo );
}
public void addSubscription( RequestCallInfo requestCallInfo )
{
subscriptions.add( requestCallInfo );
}
}
@Override
public void sendRequest( boolean fUseCache, boolean fInvalidate, RequestDesc request, Object cookie, XRPCRequest callback )
{
// destroys the data cache if specified so
if( fInvalidate )
{
//GWT.log( "CACHE-CLEAR for request " + request.getUniqueKey() + " / " + request.getExtraInfo() );
cache.clear();
}
RequestCallInfo requestCallInfo = new RequestCallInfo( request, callback, cookie );
requestStack.add( requestCallInfo );
if( fUseCache )
{
//GWT.log( "CACHE-STATE for request " + request.getUniqueKey() + " / " + request.getExtraInfo() + " size:" + cache.size() + " hasResponse:" + cache.containsKey( request.getUniqueKey() ) );
// is the result already in cache ?
ResponseJSO cached = cache.get( request.getUniqueKey() );
if( cached != null )
{
requestCallInfo.setResult( 0, null, null, cached );
checkAnswersToGive();
return;
}
// is the same request already pending ?
for( PendingRequestInfo pending : pendingRequests )
{
if( !pending.requestKey.equals( request.getUniqueKey() ) )
continue;
pending.addSubscription( requestCallInfo );
return;
}
}
// create a pending request
PendingRequestInfo pending = new PendingRequestInfo( fUseCache && (!fInvalidate), requestCallInfo );
pendingRequests.add( pending );
// send the request to the server
srv.sendRequest( request, pending, this );
}
// receives the answer from the ServerComm object
@Override
public void onResponse( Object cookie, ResponseJSO response, int msgLevel, String msg )
{
PendingRequestInfo info = (PendingRequestInfo) cookie;
// Store answer in cache
if( info.fStoreResultInCache )
cache.put( info.requestKey, response );
// give the result to all the subscribees
for( RequestCallInfo call : info.subscriptions )
call.setResult( msgLevel, msg, null, response );
// forget this request
pendingRequests.remove( info );
// calls back the clients
checkAnswersToGive();
}
private void checkAnswersToGive()
{
if( fCallbackingScheduled )
return;
fCallbackingScheduled = true;
Scheduler.get().scheduleFinally( checkResults );
}
ScheduledCommand checkResults = new ScheduledCommand()
{
@Override
public void execute()
{
fCallbackingScheduled = false;
while( !requestStack.isEmpty() )
{
RequestCallInfo info = requestStack.get( 0 );
// we give back results in the order requests have been made,
// here we may need to wait for other responses to arrive
if( !info.fResultReceived )
{
GWT.log( "cannot give further results are we are still waiting for previous one..." );
return;
}
requestStack.remove( 0 );
info.giveResultToCallbacks();
}
}
};
}