package fr.lteconsulting.hexa.client.comm; import java.util.ArrayList; import java.util.List; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONNumber; import com.google.gwt.json.client.JSONObject; import fr.lteconsulting.hexa.client.comm.RPCBatchRequestSender.XRPCBatchRequestSender; import fr.lteconsulting.hexa.client.interfaces.IAsyncCallback; import fr.lteconsulting.hexa.client.tools.HexaTools; public class RPCProxy { private String baseUrl = null; private XRPCProxy callback = null; private ArrayList<XRPCProxyStatus> statusCallbacks; private ArrayList<BeforeNetworkRequestHandler> beforeNetworkRequestHandlers; private ArrayList<AfterNetworkRequestHandler> afterNetworkRequestHandlers; ArrayList<RPCBatchRequestSender> batchRequestSenders = null; boolean isSendScheduled = false; int nbSentBytes = 0; int nbReceivedBytes = 0; public RPCProxy() { batchRequestSenders = new ArrayList<RPCBatchRequestSender>(); statusCallbacks = new ArrayList<XRPCProxyStatus>(); } public void Init( String baseUrl, XRPCProxy serverCommMessageCb ) { this.baseUrl = baseUrl; this.callback = serverCommMessageCb; } public Object addBeforeNetworkRequestHandler( BeforeNetworkRequestHandler handler ) { if( beforeNetworkRequestHandlers == null ) beforeNetworkRequestHandlers = new ArrayList<BeforeNetworkRequestHandler>(); beforeNetworkRequestHandlers.add( handler ); return handler; } public void removeBeforeNetworkRequestHandler( Object registration ) { beforeNetworkRequestHandlers.remove( registration ); } public Object addAfterNetworkRequestHandler( AfterNetworkRequestHandler handler ) { if( afterNetworkRequestHandlers == null ) afterNetworkRequestHandlers = new ArrayList<AfterNetworkRequestHandler>(); afterNetworkRequestHandlers.add( handler ); return handler; } public void removeAfterNetworkRequestHandler( Object registration ) { afterNetworkRequestHandlers.remove( registration ); } public void registerStatusCallback( XRPCProxyStatus callback ) { statusCallbacks.add( callback ); } public void sendRequest( RequestDesc request, Object cookie, XRPCRequest callback ) { RPCBatchRequestSender sender = getBatchRequestSender(); sender.addRequest( new RequestCallInfo( request, callback, cookie ) ); scheduleSend(); } private void scheduleSend() { if( isSendScheduled ) return; isSendScheduled = true; Scheduler.get().scheduleDeferred( sendCommand ); } private ScheduledCommand sendCommand = new ScheduledCommand() { @Override public void execute() { isSendScheduled = false; // something to send ? if( batchRequestSenders.isEmpty() ) return; // send the first batch ready to be sent for( RPCBatchRequestSender sender : batchRequestSenders ) { if( sender.isReadyToSend() ) { sender.send(); return; } } } }; private XRPCBatchRequestSender batchSenderCallback = new XRPCBatchRequestSender() { @Override public List<BeforeNetworkRequestHandler> getBeforeNetworkRequestHandlers() { return beforeNetworkRequestHandlers; } @Override public List<AfterNetworkRequestHandler> getAfterNetworkRequestHandlers() { return afterNetworkRequestHandlers; } @Override public void error( RPCErrorCodes errorCode, Exception exception, RPCBatchRequestSender sender ) { nbReceivedBytes += sender.getNbReceivedBytes(); batchRequestSenders.remove( sender ); switch( errorCode ) { case ERROR_REQUEST_SEND: HexaTools.alert( "Error with server connexion", "Error while sending the request" ); break; case ERROR_REQUEST_RESPONSE_STATUS: HexaTools.alert( "Error with server connexion", "The remote server seems so be down.... Try refreshing the applicatio please..." ); break; case ERROR_REQUEST_RESPONSE_EMPTY: HexaTools.alert( "Error with server connexion", "Empty response from the server, connexion may have been cut !" ); break; case ERROR_REQUEST_RESPONSE_PARSE: callback.signalResultParseError( sender.getReceivedText(), sender.getTrace() ); break; case ERROR_REQUEST_GWT: callback.signalRequestError( sender.getTrace(), exception ); break; } statusRefresh(); scheduleSend(); } @Override public void answerReceived( RPCBatchRequestSender sender ) { nbReceivedBytes += sender.getNbReceivedBytes(); // TODO : I removed that because the assert was to often not // verified // TODO : reordering answers is then needed ! // int index = batchRequestSenders.indexOf( sender ); // assert index == 0; batchRequestSenders.remove( sender ); for( RequestCallInfo info : sender.sentRequests ) { // signal server message to interested callbacks if( info.msg.length() > 0 ) callback.signalServerCommMessage( info.msgLevel, info.msg ); if( info.msgLevel == 1 ) continue; // don't process replies that are in error... if( info.hangout != null ) { // the server tells us a hang out process is needed hangOut( info, info.hangout ); } else { // normal case fallback info.giveResultToCallbacks(); } } statusRefresh(); scheduleSend(); } @Override public void sent( RPCBatchRequestSender request ) { nbSentBytes += request.getNbSentBytes(); statusRefresh(); } }; // handles the case where an hangOut has been generated by the server // in this case, we give control back to the application so that the user // can provide the missing information, then, we send the hangout answer // over the network private void hangOut( final RequestCallInfo req, GenericJSO hangOut ) { final int hangOutId = hangOut.getIntByIdx( 0 ); JsArrayString tmp = hangOut.getGenericJSOByIdx( 1 ).cast(); String name = tmp.get( 0 ); String type = tmp.get( 1 ); String title = tmp.get( 2 ); String description = tmp.get( 3 ); JavaScriptObject hangOutCurrentData = hangOut.getGenericJSOByIdx( 2 ); callback.hangOut( title, description, name, type, hangOutCurrentData, new IAsyncCallback<JavaScriptObject>() { @Override public void onSuccess( JavaScriptObject result ) { JSONArray params = new JSONArray(); params.set( 0, new JSONNumber( hangOutId ) ); params.set( 1, new JSONObject( result ) ); req.request = new RequestDesc( req.request.service, req.request.interfaceChecksum, "_hang_out_reply_", params ); sendRequest( req.request, req.cookie, req.callback ); } } ); } // retrieve a batch request sender that is OK for adding requests private RPCBatchRequestSender getBatchRequestSender() { RPCBatchRequestSender sender = null; // test the last sender we have in the list if( !batchRequestSenders.isEmpty() ) { sender = batchRequestSenders.get( batchRequestSenders.size() - 1 ); if( sender.canAddRequest() ) return sender; } // otherwise, create a new one sender = new RPCBatchRequestSender(); sender.init( baseUrl, batchSenderCallback ); batchRequestSenders.add( sender ); return sender; } private void statusRefresh() { String status; int nbPending = 0; for( RPCBatchRequestSender sender : batchRequestSenders ) if( sender.isSending() ) nbPending++; if( nbPending == 0 ) status = "OK"; else status = "Loading"; for( XRPCProxyStatus callback : statusCallbacks ) callback.onServerCommStatusChanged( status, nbPending, nbSentBytes, nbReceivedBytes ); } }