Announcement

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

    Bug: Way to much server action for DMI custom validation

    Hi Isomorphic,

    please have a look in this testcase (added to built-in-ds), where I think that the DMI-Validator is called way to often:

    SQL (Oracle):
    Code:
    DROP TABLE t_test CASCADE CONSTRAINTS ;
    DROP SEQUENCE T_TEST_ID ;
     CREATE TABLE t_test
        (
          id     INTEGER NOT NULL,
          field1 VARCHAR2(1 CHAR) NOT NULL CHECK(field1 IN('N', 'Y')),
          field2 VARCHAR2(1 CHAR) NOT NULL CHECK(field2 IN('N', 'Y'))
        ) ;
    ALTER TABLE t_test ADD CONSTRAINT CK_t_test_field1_field2 CHECK
    (
      field1 <> field2
    )
    ;
    ALTER TABLE t_test ADD CONSTRAINT PK_test PRIMARY KEY
    (
      id
    )
    ;
    CREATE SEQUENCE T_TEST_ID NOCACHE ORDER ;
    CREATE OR REPLACE TRIGGER t_test_BI BEFORE
       INSERT ON t_test FOR EACH ROW WHEN(NEW.id IS NULL) BEGIN
       SELECT T_TEST_ID.NEXTVAL INTO :NEW.id FROM DUAL;
    END;
    /
     INSERT INTO T_TEST
        (field1, field2
        ) VALUES
        ('N', 'Y'
        ) ;
    COMMIT ;
    Java Client:
    Code:
    package com.smartgwt.sample.client;
    
    import com.smartgwt.client.data.DataSource;
    import com.smartgwt.client.widgets.IButton;
    import com.smartgwt.client.widgets.events.ClickEvent;
    import com.smartgwt.client.widgets.events.ClickHandler;
    import com.smartgwt.client.widgets.grid.ListGrid;
    import com.smartgwt.client.widgets.layout.VLayout;
    import com.google.gwt.core.client.EntryPoint;
    
    public class Testcase implements EntryPoint {
    	@Override
    	public void onModuleLoad() {
    		VLayout vL = new VLayout() {
    			{
    				setWidth100();
    				setHeight100();
    				final DataSource testDS = DataSource.get("T_TEST2");
    
    				final ListGrid testLG = new ListGrid() {
    					{
    						setDataSource(testDS);
    						fetchData();
    						setCanEdit(true);
    					}
    				};
    				final IButton btn = new IButton("Reload", new ClickHandler() {
    					@Override
    					public void onClick(ClickEvent event) {
    						testDS.invalidateCache();
    						testLG.invalidateCache();
    						testLG.fetchData();
    						testLG.clearEditValue(0, "FIELD1");
    						testLG.clearEditValue(0, "FIELD2");
    						testLG.clearEditValue(1, "FIELD1");
    						testLG.clearEditValue(1, "FIELD2");
    						testLG.clearFieldError(0, "FIELD1");
    						testLG.clearFieldError(0, "FIELD2");
    						testLG.clearFieldError(1, "FIELD1");
    						testLG.clearFieldError(1, "FIELD2");
    					}
    				});
    				setMembersMargin(10);
    				addMember(testLG);
    				addMember(btn);
    			}
    		};
    		vL.draw();
    	}
    }
    Java Server:
    Code:
    package com.smartgwt.sample.server;
    
    import java.util.Map;
    
    import com.isomorphic.datasource.DataSource;
    import com.isomorphic.datasource.Validator;
    import com.isomorphic.log.Logger;
    import com.isomorphic.util.DataTools;
    
    public class ValidatorEitherField1Field2 {
    	Logger log = new Logger("com.isomorphic.Logme");
    
    	public boolean condition(Object value, Validator validator, String fieldName, Map record, DataSource ds)
    			throws Exception {
    		log.warn("validating value: '" + value + "' for fieldName: '" + fieldName + "' in DataSource: '" + ds.getID()
    				+ "'\nin record: " + DataTools.prettyPrint(record));
    		boolean isField1;
    		boolean isField2;
    
    		if (record.containsKey("FIELD1") && record.containsKey("FIELD2")) {
    			log.warn("FIELD1:" + record.get("FIELD1") + "\n" + //
    					"FIELD2:" + record.get("FIELD2") + "\n");
    			isField1 = (Boolean) record.get("FIELD1");
    			isField2 = (Boolean) record.get("FIELD2");
    		} else {
    			@SuppressWarnings("unchecked")
    			Map<String, Object> m = (Map<String, Object>) ds.fetchById(record.get(ds.getPrimaryKey()));
    			log.warn("Issued manual FETCH!");
    			if (record.containsKey("FIELD1"))
    				isField1 = (Boolean) record.get("FIELD1");
    			else
    				isField1 = (Boolean) m.get("FIELD1");
    
    			if (record.containsKey("FIELD2"))
    				isField2 = (Boolean) record.get("FIELD2");
    			else
    				isField2 = (Boolean) m.get("FIELD2");
    		}
    
    		if (isField1 ^ isField2)
    			return true;
    
    		else {
    			validator.addErrorMessageVariable("field1", String.valueOf(isField1));
    			validator.addErrorMessageVariable("field2", String.valueOf(isField2));
    			return false;
    		}
    	}
    };
    .ds.xml:
    Code:
    <DataSource schema="testcase" dbName="Oracle" tableName="T_TEST" ID="T_TEST2" dataSourceVersion="1"
    	serverType="sql"
    >
    	<fields>
    		<field primaryKey="true" name="ID" type="sequence"></field>
    		<field name="FIELD1" length="1" type="boolean" sqlStorageStrategy="singleChar" sqlTrueValue="Y" sqlFalseValue="N"
    			required="true"
    		>
    			<validators>
    				<validator type="serverCustom">
    					<serverObject lookupStyle="new" className="com.smartgwt.sample.server.ValidatorEitherField1Field2" />
    					<errorMessage>Field1: $field1, Field2: $field2</errorMessage>
    				</validator>
    			</validators>
    		</field>
    		<field name="FIELD2" length="1" type="boolean" sqlStorageStrategy="singleChar" sqlTrueValue="Y" sqlFalseValue="N"
    			required="true"
    		>
    			<validators>
    				<validator type="serverCustom">
    					<serverObject lookupStyle="new" className="com.smartgwt.sample.server.ValidatorEitherField1Field2" />
    					<errorMessage>Field1: $field1, Field2: $field2</errorMessage>
    				</validator>
    			</validators>
    		</field>
    	</fields>
    </DataSource>
    Serverlog:
    Code:
    === 2012-04-19 23:19:27,899 [l0-5] INFO  Download - Returning 304: Not modified on conditional get of: C:\Users\sta\workspace\built-in-ds_testcase2\war\builtinds\sc\skins\Enterprise\images\DynamicForm\unchecked_Down.png
    === 2012-04-19 23:19:28,731 [l0-5] INFO  Download - Returning 304: Not modified on conditional get of: C:\Users\sta\workspace\built-in-ds_testcase2\war\builtinds\sc\skins\Enterprise\images\DynamicForm\checked_Down.png
    === 2012-04-19 23:19:29,657 [l0-5] INFO  RequestContext - URL: '/builtinds/sc/IDACall', User-Agent: 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko/20100101 Firefox/11.0': Moz (Gecko) with Accept-Encoding header
    === 2012-04-19 23:19:29,664 [l0-5] DEBUG XML - Parsed XML from (in memory stream): 4ms
    === 2012-04-19 23:19:29,669 [l0-0] INFO  RequestContext - URL: '/builtinds/sc/IDACall', User-Agent: 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko/20100101 Firefox/11.0': Moz (Gecko) with Accept-Encoding header
    === 2012-04-19 23:19:29,671 [l0-5] DEBUG RPCManager - Processing 2 requests.
    === 2012-04-19 23:19:29,672 [l0-5] DEBUG RPCManager - Request #1 (DSRequest) payload: {
        values:{
            FIELD2:false,
            ID:1,
            FIELD1:true,
            _selection_1:true
        },
        operationConfig:{
            dataSource:"T_TEST2",
            operationType:"validate"
        },
        validationMode:"partial",
        appID:"builtinApplication",
        operation:"T_TEST2_validate",
        oldValues:{
            FIELD2:false,
            ID:1,
            FIELD1:true,
            _selection_1:true
        },
        criteria:{
        }
    }
    === 2012-04-19 23:19:29,673 [l0-5] DEBUG RPCManager - Request #2 (DSRequest) payload: {
        values:{
            FIELD2:false,
            ID:1,
            FIELD1:true,
            _selection_1:true
        },
        operationConfig:{
            dataSource:"T_TEST2",
            operationType:"validate"
        },
        validationMode:"partial",
        appID:"builtinApplication",
        operation:"T_TEST2_validate",
        oldValues:{
            FIELD2:false,
            ID:1,
            FIELD1:true,
            _selection_1:true
        },
        criteria:{
        }
    }
    === 2012-04-19 23:19:29,673 [l0-0] DEBUG XML - Parsed XML from (in memory stream): 2ms
    === 2012-04-19 23:19:29,673 [l0-5] INFO  IDACall - Performing 2 operation(s)
    === 2012-04-19 23:19:29,673 [l0-5] DEBUG AppBase - [builtinApplication.T_TEST2_validate] No userTypes defined, allowing anyone access to all operations for this application
    === 2012-04-19 23:19:29,673 [l0-5] DEBUG AppBase - [builtinApplication.T_TEST2_validate] No public zero-argument method named '_T_TEST2_validate' found, performing generic datasource operation
    === 2012-04-19 23:19:29,676 [l0-0] DEBUG RPCManager - Processing 1 requests.
    === 2012-04-19 23:19:29,677 [l0-0] DEBUG RPCManager - Request #1 (DSRequest) payload: {
        criteria:{
            ID:1
        },
        values:{
            ID:1,
            FIELD1:true,
            FIELD2:false
        },
        operationConfig:{
            dataSource:"T_TEST2",
            operationType:"update"
        },
        componentId:"isc_Testcase_1$1_0",
        appID:"builtinApplication",
        operation:"T_TEST2_update",
        oldValues:{
            FIELD2:true,
            ID:1,
            FIELD1:false
        }
    }
    === 2012-04-19 23:19:29,677 [l0-0] INFO  IDACall - Performing 1 operation(s)
    === 2012-04-19 23:19:29,677 [l0-0] DEBUG AppBase - [builtinApplication.T_TEST2_update] No userTypes defined, allowing anyone access to all operations for this application
    === 2012-04-19 23:19:29,677 [l0-0] DEBUG AppBase - [builtinApplication.T_TEST2_update] No public zero-argument method named '_T_TEST2_update' found, performing generic datasource operation
    === 2012-04-19 23:19:29,678 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] validating value: 'false' for fieldName: 'FIELD2' in DataSource: 'T_TEST2'
    in record: {
        FIELD2:false,
        ID:1,
        FIELD1:true,
        _selection_1:true
    }
    === 2012-04-19 23:19:29,678 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] FIELD1:true
    FIELD2:false
    
    === 2012-04-19 23:19:29,678 [l0-0] WARN  Logme - [builtinApplication.T_TEST2_update] validating value: 'false' for fieldName: 'FIELD2' in DataSource: 'T_TEST2'
    in record: {
        ID:1,
        FIELD1:true,
        FIELD2:false
    }
    === 2012-04-19 23:19:29,678 [l0-0] WARN  Logme - [builtinApplication.T_TEST2_update] FIELD1:true
    FIELD2:false
    
    === 2012-04-19 23:19:29,679 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] validating value: 'true' for fieldName: 'FIELD1' in DataSource: 'T_TEST2'
    in record: {
        FIELD2:false,
        ID:1,
        FIELD1:true,
        _selection_1:true
    }
    === 2012-04-19 23:19:29,679 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] FIELD1:true
    FIELD2:false
    
    === 2012-04-19 23:19:29,679 [l0-5] DEBUG AppBase - [builtinApplication.T_TEST2_validate] No userTypes defined, allowing anyone access to all operations for this application
    === 2012-04-19 23:19:29,679 [l0-5] DEBUG AppBase - [builtinApplication.T_TEST2_validate] No public zero-argument method named '_T_TEST2_validate' found, performing generic datasource operation
    === 2012-04-19 23:19:29,680 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] validating value: 'false' for fieldName: 'FIELD2' in DataSource: 'T_TEST2'
    in record: {
        FIELD2:false,
        ID:1,
        FIELD1:true,
        _selection_1:true
    }
    === 2012-04-19 23:19:29,680 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] FIELD1:true
    FIELD2:false
    
    === 2012-04-19 23:19:29,680 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] validating value: 'true' for fieldName: 'FIELD1' in DataSource: 'T_TEST2'
    in record: {
        FIELD2:false,
        ID:1,
        FIELD1:true,
        _selection_1:true
    }
    === 2012-04-19 23:19:29,680 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] FIELD1:true
    FIELD2:false
    
    === 2012-04-19 23:19:29,681 [l0-5] DEBUG RPCManager - Content type for RPC transaction: text/plain; charset=UTF-8
    === 2012-04-19 23:19:29,681 [l0-5] DEBUG RPCManager - non-DMI response, dropExtraFields: false
    === 2012-04-19 23:19:29,682 [l0-0] WARN  Logme - [builtinApplication.T_TEST2_update] validating value: 'true' for fieldName: 'FIELD1' in DataSource: 'T_TEST2'
    in record: {
        ID:1,
        FIELD1:true,
        FIELD2:false
    }
    === 2012-04-19 23:19:29,682 [l0-0] WARN  Logme - [builtinApplication.T_TEST2_update] FIELD1:true
    FIELD2:false
    
    === 2012-04-19 23:19:29,682 [l0-5] DEBUG RPCManager - non-DMI response, dropExtraFields: false
    === 2012-04-19 23:19:29,682 [l0-0] INFO  SQLDataSource - [builtinApplication.T_TEST2_update] Performing update operation with
    	criteria: {ID:1}	values: {ID:1,FIELD1:true,FIELD2:false}
    === 2012-04-19 23:19:29,684 [l0-5] INFO  Compression - /builtinds/sc/IDACall: 246 -> 134 bytes
    === 2012-04-19 23:19:29,700 [l0-0] DEBUG PoolableSQLConnectionFactory - [builtinApplication.T_TEST2_update] Returning pooled Connection
    === 2012-04-19 23:19:29,702 [l0-0] DEBUG SQLTransaction - [builtinApplication.T_TEST2_update] Started new Oracle transaction "1928930324"
    === 2012-04-19 23:19:29,702 [l0-0] INFO  SQLDriver - [builtinApplication.T_TEST2_update] Executing SQL update on 'Oracle': UPDATE testcase.T_TEST SET FIELD1='Y', FIELD2='N' WHERE (T_TEST.ID='1')
    === 2012-04-19 23:19:29,735 [l0-0] DEBUG SQLDataSource - [builtinApplication.T_TEST2_update] update operation affected 1 rows
    === 2012-04-19 23:19:29,736 [l0-0] INFO  SQLDataSource - [builtinApplication.T_TEST2_update] primaryKeys: {ID=1}
    === 2012-04-19 23:19:29,736 [l0-0] DEBUG AppBase - [builtinApplication.T_TEST2_update, builtinApplication.null] No userTypes defined, allowing anyone access to all operations for this application
    === 2012-04-19 23:19:29,736 [l0-0] DEBUG AppBase - [builtinApplication.T_TEST2_update, builtinApplication.null] No public zero-argument method named '_null' found, performing generic datasource operation
    === 2012-04-19 23:19:29,737 [l0-0] INFO  SQLDataSource - [builtinApplication.T_TEST2_update, builtinApplication.null] Performing fetch operation with
    	criteria: {ID:1}	values: {ID:1}
    === 2012-04-19 23:19:29,737 [l0-0] INFO  SQLDataSource - [builtinApplication.T_TEST2_update, builtinApplication.null] derived query: SELECT $defaultSelectClause FROM $defaultTableClause WHERE $defaultWhereClause
    === 2012-04-19 23:19:29,738 [l0-0] INFO  SQLDataSource - [builtinApplication.T_TEST2_update, builtinApplication.null] Executing SQL query on 'Oracle': SELECT T_TEST.FIELD1, T_TEST.FIELD2, T_TEST.ID FROM testcase.T_TEST WHERE (T_TEST.ID='1')
    === 2012-04-19 23:19:29,738 [l0-0] INFO  SQLDriver - [builtinApplication.T_TEST2_update, builtinApplication.null] Executing SQL query on 'Oracle': SELECT T_TEST.FIELD1, T_TEST.FIELD2, T_TEST.ID FROM testcase.T_TEST WHERE (T_TEST.ID='1')
    === 2012-04-19 23:19:29,740 [l0-0] INFO  DSResponse - [builtinApplication.T_TEST2_update, builtinApplication.null] DSResponse: List with 1 items
    === 2012-04-19 23:19:29,740 [l0-0] INFO  DSResponse - [builtinApplication.T_TEST2_update] DSResponse: List with 1 items
    === 2012-04-19 23:19:29,740 [l0-0] DEBUG RPCManager - Content type for RPC transaction: text/plain; charset=UTF-8
    === 2012-04-19 23:19:29,740 [l0-0] DEBUG SQLTransaction - Committing Oracle transaction "1928930324"
    === 2012-04-19 23:19:29,741 [l0-0] DEBUG RPCManager - non-DMI response, dropExtraFields: false
    === 2012-04-19 23:19:29,742 [l0-0] DEBUG SQLTransaction - Ending Oracle transaction "1928930324"
    === 2012-04-19 23:19:29,743 [l0-0] INFO  Compression - /builtinds/sc/IDACall: 173 -> 151 bytes
    Doubleclick the row to enter row-edit-mode and change the values of both field1 and field2. Leave the row to issue the update. As you can see, for this single update, the Validator is called four times where I'd expect two should be enough.
    When you change only one field, the validator is called two times.

    This might be related to this forum entry http://forums.smartclient.com/showth...lidator+called

    Best regards,
    Blama

    Version: SC_SNAPSHOT-2011-12-05/EVAL Deployment 2011-12-05
    Last edited by Blama; 19 Apr 2012, 13:32.

    #2
    Because a validator is allowed to look at the whole record hence possibly depends on more than one field, all serverCustom validators are invoked any time server-side validation is performed. Does this explain what you're seeing?

    Comment


      #3
      OK, now I get the reason why this is happening for the case where I edit one field. If I edit both fields, the validator is called 4 times:
      Change of field1 -> Call all customs validators with full record (2 calls)
      Change of field2 -> Call all customs validators again with same full record (2 calls)

      As the validators do the same twice, no different result is to be expected.

      Comment


        #4
        Entry 1&3 and 2&4 are the same:

        Code:
        === 2012-04-19 23:51:37,721 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] validating value: 'true' for fieldName: 'FIELD2' in DataSource: 'T_TEST2'
        in record: {
            FIELD2:true,
            ID:1,
            FIELD1:false,
            _selection_1:true
        }
        === 2012-04-19 23:51:37,722 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] validating value: 'false' for fieldName: 'FIELD1' in DataSource: 'T_TEST2'
        in record: {
            FIELD2:true,
            ID:1,
            FIELD1:false,
            _selection_1:true
        }
        === 2012-04-19 23:51:37,722 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] validating value: 'true' for fieldName: 'FIELD2' in DataSource: 'T_TEST2'
        in record: {
            FIELD2:true,
            ID:1,
            FIELD1:false,
            _selection_1:true
        }
        === 2012-04-19 23:51:37,723 [l0-5] WARN  Logme - [builtinApplication.T_TEST2_validate] validating value: 'false' for fieldName: 'FIELD1' in DataSource: 'T_TEST2'
        in record: {
            FIELD2:true,
            ID:1,
            FIELD1:false,
            _selection_1:true
        }

        Comment

        Working...
        X