Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    How to use RPCManager.startQueue() / .sendQueue() on client-side correctly?

    I use SmartGWT 3.0 Power.

    I have a ListGrid with about 50.000 records. After filtering and clicking a button, I want to update the records in the database (change field 'isActive' from '1' to '0').

    This works for "some" (e.g. 20 selected records). However, it does not work for 100s or 1000s of selected records.

    I already activated dataRecordTable.setDataFetchMode(FetchMode.BASIC); to load ALL data. So that is not the problem I think.

    Of course the code was not efficient for 1000s of records (I was just calling updateData(record); for every single record in a for-loop). Probably, this is why it crashes.
    I do NOT have any DMI class for this datasource yet. I do not need it because I just want to do a simple update of a field. I need some kind of batching, i.e. just one HTTP request.

    After looking at the SmartGWT Quick Start Guide, I think I have to use RPCManager.startQueue and .sendQueue on client-side (as shown in the example below), right? Unfortunately, documentation is very short about this topic.

    Anyway, it does not work for me. The application freezes after some seconds and nothing is happening. I have to refresh the browser to restart the app. Database content did not change either.

    What is wrong with my code? Do I need to write a server-side DMI class or anything else?
    (again: the code works, but just for "some" records, so I think it is still doing one HTTP request for each record instead of batching / queuing?)

    Here is the code:
    Code:
    deactivateFilteredImportsButton.addClickHandler(new ClickHandler() {
    			
    	@Override
    	public void onClick(ClickEvent event) {
    				
    		final Record[] selectedRecords = dataRecordTable.getRecords();
    		int size = selectedRecords.length;
    		if (size > 0) {
    			SC.ask("Deactivate filtered imports (i.e. all records you see at the moment)?", new BooleanCallback() {
    
    				@Override
    				public void execute(Boolean value) {
    					if (value) {
    						RPCManager.startQueue();
    						for (int i=0; i<selectedRecords.length; i++) {
    							Record record = (Record) selectedRecords[i];
    							record.setAttribute(COLS.workloadActive, 0);
    							AppController.WORKLOAD.updateData(record);
    									
    						}
    						RPCManager.sendQueue();
    								
    							
    							}
    						}
    					});
    		} else {
    			SC.warn("No imports selected for deactivation");
    				}	
    				
    			}
    		});
    An additional question is, if I can solve this WITHOUT dataRecordTable.setDataFetchMode(FetchMode.BASIC); ??? I do not want to load all data at the beginning.

    Thanks for help...
    Last edited by KaiW; 4 Sep 2012, 05:07.

    #2
    Create a custom operationBinding for 'update' where you change that value from 0 to 1. In your code, put some flag into the UI that sets the 'update' function to the custom operationBinding. Update calls can take more than 1 record. Just update them all at once using the custom operationBinding and you won't have to do anything special at all.

    Comment


      #3
      Thank you for your help.

      Do you mean configure the operation binding in a xyz.ds.xml datasource. What do I have to write in the UI to do these updates at once?

      Can you please give a small example? I do not have that deep knowledge of SmartGWT :-(

      Comment


        #4
        At this data volume, you should just do a single update, not just because of the much smaller data sent from client to server but also because it would be silly to do 2000 separate database UPDATE statements over on the server.

        You can either send search criteria that identify the records by their common data values, or Criteria that just list out all the primaryKeys as the criteria. Set allowMultiUpdate:true either on the operationBinding or programmatically on the DSRequest (server-side) in order to allow the multi-record update to be processed.

        You will also want to directly update the client-side records instead of depending on cache sync to do it.

        Comment


          #5
          After several hours, I solved it.

          To help other guys, I post snippets from the code of my client view and DMI class in the following. I just send all IDs of my table from client to server via one Record and one updateData() call on my datasource.

          The server overwrites the update method and also uses just one update call to update all relevant columns in the database.

          Button on client-side:

          Code:
          		deactivateFilteredImportsButton.addClickHandler(new ClickHandler() {
          			
          			@Override
          			public void onClick(ClickEvent event) {
          				
          				final Record[] selectedRecords = dataRecordTable.getRecords();
          				int size = selectedRecords.length;
          				if (size > 0) {
          					SC.ask("Deactivate filtered imports (i.e. all records you see at the moment)?", new BooleanCallback() {
          
          						@Override
          						public void execute(Boolean value) {
          							if (value) {
          								
          								Record deactivationRecord = new Record();
          								final List<Integer> deactivationList = new LinkedList<Integer>();
          								
          								for (int i=0; i<selectedRecords.length; i++) {
          									Record record = (Record) selectedRecords[i];
          									Integer workloadIdToDeactivate = record.getAttributeAsInt(COLS.workloadID);
          									
          									deactivationList.add(workloadIdToDeactivate);
          								}
          								
          								deactivationRecord.setAttribute("workloadIDsToDeactivate", deactivationList.toArray(new Integer[deactivationList.size()]));
          									
          									AppController.WORKLOADDEACTIVATION.updateData(deactivationRecord, new DSCallback() {
          
          										@Override
          										public void execute(DSResponse response,Object rawData, DSRequest request) {
          											
          											dataRecordTable.invalidateCache();
          //											closeWindow();
          											SC.say("Deactivation of " + deactivationList.size() + " workloads was successful.");
          										}
          										
          									});
          									
          								
          								supplierChanged(currentUserID);
          							}
          						}
          					});
          				} else {
          					SC.warn("No imports selected for deactivation");
          				}	
          				
          			}
          		});
          And the DMI class:

          Code:
          public class WorkloadDeactivationDMI {
          
          	private static Logger logger = Logger.getLogger(WorkloadDeactivationDMI.class);
          
          
          	public DSResponse update(DSRequest dsRequest, HttpServletRequest request) {
          
          		logger.debug("Workload DMI update called (for deactivation of workloads).");
          
          		Map<String, Object> values = dsRequest.getValues();
          
          		List<Long> workloadIDsToDeactivateList = (List<Long>) values.get("workloadIDsToDeactivate");
          
          		logger.info("Start deactivating " + workloadIDsToDeactivateList.size() + " workloads"); // => wird 2x aufgerufen => beim 2.mal NPE
          	
          		Map<String, Object> workloadToUpdate = new HashMap<String, Object>();
          		workloadToUpdate.put(COLS.workloadActive, 0);
          
          
          		// Use initial RPCManager:
          		RPCManager rpcManager = dsRequest.getRPCManager();
          		DSRequest newDsRequest = new DSRequest("WORKLOAD", "update");
          		newDsRequest.setRPCManager(rpcManager);
          		newDsRequest.setCriteria(COLS.workloadID, workloadIDsToDeactivateList);
          		newDsRequest.setAllowMultiUpdate(true);
          		newDsRequest.setValues(workloadToUpdate);
          		
          		// Attention: locally (i.e. using Eclipse / GWT / Jetty instead of deployment on Tomcat / Production Server) can cause an exception if 
          		// too many workloads are selected:
          		// ERROR IDACall - Top-level servlet error: java.lang.IllegalStateException: Form too large237172>200000
          		// This is no application / SmartGWT error, but a Jetty limitation!
          		try {
          			newDsRequest.execute();
          		} catch (Exception e) {
          			logger.error("Failed to deactivate " + workloadIDsToDeactivateList.size() + " workloads", e);
          		}
          
          
          		logger.info("finished deactivating" + workloadIDsToDeactivateList.size() + " workloads");
          
          		return new DSResponse(DSResponse.STATUS_SUCCESS);
          	}
          
          }

          If anyone sees any improvements (patterns, performance, etc.), please tell me...
          For example, I am not sure, if I need to set the RPCManager in the DMI class, or if I can do any other performance improvements?

          Comment


            #6
            It should also work to just call updateData() client-side with a Record with multiple values set for the primaryKey, which would mean no server code at all (other than a declarative allowMultiUpdate setting on the operationBinding).

            Comment

            Working...
            X