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

    Using variables in velocity expression of DS field validator


    I am experimenting with the velocity expression validation which can be defined on a DS field. We are using the latest nightly and testing on FireFox.

    I have managed to get a basic expression working on one of my DS fields. The expression that is working uses the $record variable and checks for some dummy text string to be present in the text field.

    $util.contains($record.reference.text, 'test')
    (The reference field is one of our own simple types, which has a text member variable)

    I tried to go to the next level by referencing another field of the DS, beyond the record scope. But all my attempts are failing. I have tried a number of combinations as follows, they are all giving me nullpointer errors on the 'contains' function or syntax errors.

    $util.contains($dataSource.incidents(0).reference.text, 'test')
    $util.contains($dataSource.incidents[0].reference.text, 'test')
    $util.contains($dataSource.incidents.get(0).reference.text, 'test')
    $util.contains($ListTool($dataSource.incidents, 0).reference.text, 'test')
    I access the reference field because I know that it was working using the simple expression. In real life I will be accessing other field of the DS.

    These are the relevant extracts of our DS definitions.

    <DataSource ID="FineSessDTO__-1_4_1_100011_100008" dropExtraFields="true" sparseUpdates="true">
    <field name="sessDtoId" canEdit="false" defaultValue="-2000000" type="integer" primaryKey="true" required="true" hidden="true" />
    <field name="fines" type="247__-1_4_1_100011_100008" javaClass="dto.incident.FineDTO" multiple="true" />
    <field name="appeals" type="384__-1_4_1_100011_100008" javaClass="dto.incident.AppealDTO" multiple="true" />
    <field name="fineFees" type="403__-1_4_1_100011_100008" javaClass="dto.contract.FineFeeDTO" multiple="true" />
    <field name="incidents" type="237__-1_4_1_100011_100008" javaClass="dto.incident.IncidentDTO" multiple="true" />
    <field name="involvedParties" type="383__-1_4_1_100011_100008" javaClass="dto.incident.InvolvedPartyDTO" multiple="true" />
    <serverObject ID="FineSessStore" lookupStyle="new" className="server.stores.fine.FineSessStore"/>
    	<binding operationId="IFineOutpost.checkIncident" operationType="custom" serverMethod="checkIncident"/>
    	<binding operationId="IFineOutpost.completeIncident" operationType="custom" serverMethod="completeIncident"/>
    	<binding operationId="IFineOutpost.getNewFine" operationType="custom" serverMethod="getNewFine"/>
    	<binding operationId="IFineOutpost.generateNewInstance" operationType="fetch" serverMethod="generateNewInstance"/>
    This is the definition of the 'incidents' instance I am referencing in my expression.

    <DataSource ID="237__-1_4_1_100011_100008" dropExtraFields="true" sparseUpdates="true">
    <!-- more fields -->
    <field sraId="1496" name="reference" title="reference" type="silkString" javaClass="parameter.SraString" isBusinessKey="true" crud="15" >
    <validator type="serverCustom" validateOnChange="true"><serverCondition><![CDATA[$util.contains($dataSources.incidents[0].reference.text, 'jasa')]]></serverCondition><errorMessage>This is a validation test</errorMessage></validator></validators>
    <!-- more fields -->
    Could you please indicate what I am doing wrong here. Is it the array access which I am approaching incorrectly or is it the $dataSource variable that needs another syntax or something?

    thanks a lot!
    Last edited by bade; 17 Apr 2011, 09:32.

    Well, those are inventive expressions, but the right format is shown in the Velocity Expression sample here. When you use $dataSources.someDataSourceId (note $dataSources not $dataSource), the object returned is a DataSource instance, not an array. It has APIs like findById() (as shown in the sample).


      Thanks, we need to be creative in our jobs from time to time don't we.

      I did had a look at that sample but I was trying to avoid a re-fetch of the data (which is what the $dataSources approach is implying). Looking at the DS Request I understand that only a part of the data is sent over and that is the data to which I am limited to use without actually fetching additional stuff.

      Anyway, I started to play around with the '$dataSources' syntax but ran into a little issue. It seems that the DSRequest is not including the HttpServletRequest, which we rely on to get session information (when executing a fetch). I call this method 'DSRequest.getHttpServletRequest()' but its returning me 'null'.

      Do I need to specify a flag in the velocity expression?



        You're correct, the data in $record is just the data sent by the browser. Remember, you're validating data, so you can't rely on the inbound data if you need to check, for example, some field of a related record in order to enforce a security rule. It should be fetched from your data store, and that's what $dataSources enables easily.

        On accessing the HttpServletRequest, show the actual Velocity expression you attempted to use. The docs tell you it should be $servletRequest.


          This is the expression I am using,
          $value <= $dataSources.FineSessDTO__-1_4_1_100011_100008.fetchById($
          The Java implementation of our fetch relies on the HttpServletRequest to extract a session hash to identify the user. In our code we call the method 'DSRequest.getHttpServletRequest()' to retrieve it.

          Can I pass that information ($servletRequest) in the 'fetchByID' call?

          Thanks very much!


            You can create your own instance method that takes a servletRequest and an ID and returns the record.

            But note that having your DataSource operations depend on the servletRequest is bad practice. It limits their utility (command-line et al use cases are not possible) and in various situations like this, you need to come up with a contrived way to pass in a servletRequest.


              Ok, I see. That approach requires some additional changes on our end but I will have a go at it when I get the chance.

              I understand that the dependency on the servlet request is not optimal. Could you point me to a better approach? We basically want to stick a 'session hash' in the DS Requests/Responses in order to be able to check it when we need to.


                What do you mean by a "session hash"? session id? For what purpose, security checks?


                  Yes indeed, it is a session ID. We use it to determine the user and his relevant settings (currency, language, company) on the server side. Our (legacy) application layer which is built using EJBs requires this information to function properly, one of the things it performs is indeed security checks.

                  Now we stick this session ID or hash in a cookie, which was working fine. Having a built in feature that we can leverage would sure be fine for us. We do need to be able to access the session ID in both RPC and DS requests/response scenarios.



                    The recommended architecture is that security checks are performed *before* entering the data layer (DataSources). This way, DataSources can be used directly in command line tools and other scenarios.


                      do you have samples of such 'command line tools'?


                        Do you mean you can't think of use cases?

                        Two examples are:

                        1. Doing what the Admin Console does from the command-line - regeneration of tables and import of sample data

                        2. Sending a batch of notifications out about recent activity in the database


                          I was more thinking if it would mean that you could create a very easy integration testing tool:

                          get a DataSource
                          perform fetch
                          perform update
                          perform fetch
                          and other actions on that DataSource,
                          all in a java standalone program.

                          And somehow it would connect to a running webapp container to get into the SmartClient java webserver code which handles the actions almost the same as if it were coming from the IDACall servlet.


                            That too. But the earlier point made in this thread was: keep your user-specific logic out of the DataSource layer so that you can use your DataSources without having to find some way to get into the context of a servlet container.


                              For everyone finding this thread while searching for Velocity error messages: Please note that it is $dataSource.fetchById(...) and not $dataSource.findById(...)