You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@isis.apache.org by GESCONSULTOR - Óscar Bou <o....@gesconsultor.com> on 2013/09/07 20:05:01 UTC

Re: Using JDO helper methods to check existence of an object for a test

I've just been thinking about a way to avoid to define a Spec Transformer for every Entity class in the Domain.

As it's just "infrastructure software", I thought that was "just enough". 

The attached code allows to use just one Spec Transformer when the Glue is written like this:
-  the employee with name "PETER"
- the employee named "PETER"
- the property with reference "REF-001"
- the property referenced "REF-001"
- the product with id "PR-001" 

From there, we know that:
- The Entity singular name is "employee" (so it can be obtained from the Isis Object Specifications, reinforcing the Ubiquitous Language use on BDD specifications).
- We must search by name
- The name must be PETER



For using it, I must define a Glue like this one:

Define a Glue like this one:
@When("The company (employee with name \"[^\"]*\") has a role assigned")
public void the_company_employee_with_name(@Transform(GenericIsisJdoTransformer.class) final  Employee employee) {
   ...
}



First "proof-of-concept" code version follows this text.

It can be improved in many ways, for allowing customization through inheritance, avoiding JDO through the use of the Apache Isis query methods, etc.


But I would let you know, right to know if you think it can be useful. 

On this way, the only implemented classes  are the ones supporting the Glues (and only the Spec Transformers for more specific use cases).


HTH, 

Oscar





---------------------




package com.xms.framework.testing.integration.spectransformers;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.jdo.Query;

import org.opensaml.artifact.InvalidArgumentException;

import org.apache.isis.applib.DomainObjectContainer;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.runtime.system.context.IsisContext;
import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
import org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl;

/**
 * Requires the Gherkin's capture in the format '([entitySingularName] [(.+?)
 * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'.
 * <p>
 * For example:
 * <ul>
 * <li>the employee with name "PETER"</li>
 * <li>the employee named "PETER"</li>
 * <li>the property with reference "REF-001"</li>
 * <li>the property referenced "REF-001"</li>
 * <li>the product with id "PR-001"</li>
 * </ul>
 * <p>
 * For matching the first one we will need the following Gherkin regular
 * expression:
 * <ul>
 * <li>
 * \\@When("The company's (employee with name \"[^\"]*\") has a role assigned")</li>
 * </ul>
 * <p>
 * From there, we know that:
 * <ul>
 * <li>The Entity singular name is "employee".</li>
 * <li>We must search by name</li>
 * <li>The name must be PETER</li>
 * </ul>
 * 
 */
public class GenericIsisJdoTransformer extends NullRecognizingTransformer<Object> {

    private static Map<String, ObjectSpecification> specificationsBySingularName;

    /**
     * Tries to obtain the Entity class name, id and search field from the
     * Cucumber capture, and find it on the JDO Object Store.
     * 
     * @see com.xms.framework.testing.integration.spectransformers.
     *      NullRecognizingTransformer#transformNonNull(java.lang.String)
     */
    @Override
    protected Object transformNonNull(final String capture) {

        return this.findFromCapture(capture);

    }

    /**
     * @param entityName
     *            Name of the Entity specified on the Gherkin's capture.
     * @return
     */
    private ObjectSpecification specificationOf(final String entityName) {

        if (IsisJdoTransformer.specificationsBySingularName == null) {
            IsisJdoTransformer.specificationsBySingularName = new HashMap<String, ObjectSpecification>();

            for (final ObjectSpecification current : IsisContext.getSpecificationLoader().allSpecifications()) {

                IsisJdoTransformer.specificationsBySingularName.put(current.getSingularName().toLowerCase(), current);
            }

        }
        return IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCase());

    }

    private final Class<?> entityClassFrom(final String capture) {

        // The Entity Id will be between "".
        String entityName = "";

        final String[] recognizedPatterns = { "( with name )", "( named )", "( with reference )", "( referenced )", "( with id )" };

        for (final String currentPattern : recognizedPatterns) {
            final Pattern p = Pattern.compile(currentPattern);
            final Matcher m = p.matcher(capture);
            if (m.find()) {
                entityName = capture.substring(0, m.start());
                break;
            }
        }

        if (entityName == "") {
            throw new InvalidArgumentException(String.format("Cannot find the entity's name on the capture %s. The format must be '([entitySingularName] [with name|with reference|named|referenced] \"[entityId]\")'", capture));
        }

        final ObjectSpecification specification = this.specificationOf(entityName);
        if (specification == null) {
            throw new InvalidArgumentException(String.format("There is no Entity registered in Isis with '%s' as it's singular name", entityName));
        }
        return specification.getCorrespondingClass();

    }

    /**
     * @param capture
     *            The Gherkin's capture
     * @return
     */
    private final String filterFrom(final String capture) {
        // Find by "name".
        if (capture.matches("(.+?)name(.+?)")) {
            return String.format("name==\"%s\"", this.entityIdFrom(capture));
        }

        // Find by "reference".
        if (capture.matches("(.+?)reference(.+?)")) {
            return String.format("reference==\"%s\"", this.entityIdFrom(capture));
        }

        // Find by "id".
        if (capture.matches("(.+?)id(.+?)")) {
            return String.format("id==\"%s\"", this.entityIdFrom(capture));
        }

        throw new InvalidArgumentException(String.format("The entity id has not been found on the capture '%s'. It must be between two \" characters.", capture));
    }

    private final Object entityIdFrom(final String capture) {
        // The Entity Id will be between "".
        final Pattern p = Pattern.compile("\"(.+?)\"");
        final Matcher m = p.matcher(capture);
        if (m.find()) {
            return m.group().replace("\"", "");
        } else {
            throw new InvalidArgumentException(String.format("The entity id has not been found on the capture '%s'. It must be between two \" characters.", capture));
        }
    }

    private final Object findFromCapture(final String capture) {

        // Need to flush().
        // The Entity can be created on the same transaction.
        // If we don't flush() the changes, perhaps the entity have not been
        // saved to the object store yet, and will not be found.
        ScenarioExecution.current().service(DomainObjectContainer.class).flush();

        final Query query = ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersistenceManager().newQuery();
        query.setClass(this.entityClassFrom(capture));
        query.setFilter(this.filterFrom(capture));
        query.setUnique(true);

        final Object result = query.execute();

        return result;

    }
}




---------------------





El 27/08/2013, a las 20:24, Kevin Meyer - KMZ <ke...@kmz.co.za> escribió:

> Hi Jeremy,
> 
> If I remember correctly, Dan has examples for overriding the "Finder" 
> methods in the ToDo demo / artefact that use JDO search classes to 
> build queries.
> 
> In fact:
> 
> In the  "ToDoItemsJdo" class (which extends an 
> AbstractFactoryAndRepository):
>    // {{ notYetComplete (action)
>    @Override
>    protected List<ToDoItem> doNotYetComplete() {
>        return allMatches(
>                new QueryDefault<ToDoItem>(ToDoItem.class, 
>                        "todo_notYetComplete", 
>                        "ownedBy", currentUserName()));
>    }
>    // }}
> 
> This appears in an old copy of the repo I have in the wicket JDO 
> quickstart project.
> 
> So while you can't use "FindByPattern", JDO has implemented a 
> useful alternative.
> 
> Regards,
> Kevin
> 
> 
> On 27 Aug 2013 at 14:37, Jeremy Gurr wrote:
> 
>> I'm playing around with the cucumber support tools in isis (a testing framework for behavior driven development, for those who don't know), and have created a test that basically looks like this:
>> 
>> <snip>
>> 
>> It's very convenient that cucumber instantiates my ServiceClass model object and automatically plugs in fields according to the feature spec column header. This enables me to add new fields simply by adding a column in the spec, and adding the corresponding column in my model object. It skips the extra hassle of having to update the glue code as well as service methods to construct the object.
>> 
>> The "exists" method contains this code:
>> 
>> public boolean exists(ServiceClass serviceClass) {
>> final QueryFindByPattern<ServiceClass> query = new QueryFindByPattern<ServiceClass>(ServiceClass.class, serviceClass);
>> final ServiceClass firstMatch = firstMatch(query);
>> 
>> return firstMatch != null;
>> }
>> 
>> I'm just trying to verify that an object exists with fields matching the incoming object, some fields of which may be null, meaning that they can be any value. The QueryFindByPattern class seemed to be a perfect fit since I'm passing around ServiceClass instances anyway. However, running it executes code that is not yet implemented:
>> 
>> <snip>


Re: Using JDO helper methods to check existence of an object for a test

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
On 8 September 2013 09:21, GESCONSULTOR - Óscar Bou
<o....@gesconsultor.com>wrote:

>
> That sounds really nice...
>
>
>
> Are stress tests also included, in addition to regression tests?
>

Not sure.  Perhaps.  It'll probably depend on how well the selenium
webdrivers work.  I was thinking that using Chrome driver  [2] is good for
developing the tests (can see stuff happening), but that ghostdriver /
phantomjs [3] is probably the way to go for CI and for stress tests.



>
> That raised on the list the previous weeks.  It will be really interesting
> to know if there are any concurrency issues at Wicket, Isis or DN.
>

Isis does - in the Oid - have full support for detecting concurrency
violations, as does DataNucleus if mapped correctly.

If there are issues, it'll be because Oids encoded in the Wicket page are
stale and need refreshing.  This might mean removing some of the ajax in
the viewer and just doing a redirect-after-post to force a page refresh.



>
> Seems that Selenium can also be used in a grid configuration to simulate
> stress load tests.
>

Yup, I believe so.



>
> The creator of Selenium has founded also a company specialized on web
> stress tests [1]. As it's integrated with Amazon Web Services, you can
> configure your stress environment (and pay for it).
>

Yeah, I'm aware of that.  Will depend on how robust the geb framework is.
 Over in Ireland we're doing something similar using a framework called
Coypu.  This also sits on top of Selenium.  Our experience is that Selenium
by itself can be fragile but a layer on top - such as Coypu - can make it
much more stable by doing automatic retries.  What I I don't know is how
good geb is at doing that also.



>
> [snip]
>
>
Cheers
Dan


[2] https://code.google.com/p/selenium/wiki/ChromeDriver
[3] https://github.com/detro/ghostdriver




>
>
> El 08/09/2013, a las 09:45, Dan Haywood <da...@haywood-associates.co.uk>
> escribió:
>
> > That's fine with me.
> >
> > This week I'm starting to look at automated web browser tests (possibly
> > using Geb [1] as a layer over Selenium).  Plan is for Estatio to act as a
> > regression suite for Isis as a whole.  So no immediate rush my end.
> >
> > Cheers
> > Dan
> >
> >
> > [1] http://www.gebish.org/
> >
> >
> > On 8 September 2013 08:35, GESCONSULTOR <o....@gesconsultor.com> wrote:
> >
> >> For sure!
> >>
> >> Unless you or Jeremy plan to use it immediately let me check and improve
> >> it this week through some BDD features we must implement.
> >>
> >> Thanks,
> >>
> >> Oscar
> >>
> >>
> >> El 08/09/2013, a las 09:20, Dan Haywood <da...@haywood-associates.co.uk>
> >> escribió:
> >>
> >>> Looks very good, Oscar, exactly the sort of thing that should be in the
> >>> framework, I think.
> >>>
> >>> Could you raise a ticket and provide a commit?  If you have the time,
> >>> perhaps you could also refactor the ToDo app to show its use?
> >>>
> >>> Many thanks
> >>>
> >>> Cheers
> >>> Dan
> >>>
> >>>
> >>>
> >>> On 7 September 2013 19:05, GESCONSULTOR - Óscar Bou
> >>> <o....@gesconsultor.com>wrote:
> >>>
> >>>>
> >>>> I've just been thinking about a way to avoid to define a Spec
> >> Transformer
> >>>> for every Entity class in the Domain.
> >>>>
> >>>> As it's just "infrastructure software", I thought that was "just
> >> enough".
> >>>>
> >>>> The attached code allows to use just one Spec Transformer when the
> Glue
> >> is
> >>>> written like this:
> >>>> -  the employee with name "PETER"
> >>>> - the employee named "PETER"
> >>>> - the property with reference "REF-001"
> >>>> - the property referenced "REF-001"
> >>>> - the product with id "PR-001"
> >>>>
> >>>> From there, we know that:
> >>>> - The Entity singular name is "employee" (so it can be obtained from
> the
> >>>> Isis Object Specifications, reinforcing the Ubiquitous Language use on
> >> BDD
> >>>> specifications).
> >>>> - We must search by name
> >>>> - The name must be PETER
> >>>>
> >>>>
> >>>>
> >>>> For using it, I must define a Glue like this one:
> >>>>
> >>>> Define a Glue like this one:
> >>>> @When("The company (employee with name \"[^\"]*\") has a role
> assigned")
> >>>> public void
> >>>>
> >>
> the_company_employee_with_name(@Transform(GenericIsisJdoTransformer.class)
> >>>> final  Employee employee) {
> >>>>  ...
> >>>> }
> >>>>
> >>>>
> >>>>
> >>>> First "proof-of-concept" code version follows this text.
> >>>>
> >>>> It can be improved in many ways, for allowing customization through
> >>>> inheritance, avoiding JDO through the use of the Apache Isis query
> >> methods,
> >>>> etc.
> >>>>
> >>>>
> >>>> But I would let you know, right to know if you think it can be useful.
> >>>>
> >>>> On this way, the only implemented classes  are the ones supporting the
> >>>> Glues (and only the Spec Transformers for more specific use cases).
> >>>>
> >>>>
> >>>> HTH,
> >>>>
> >>>> Oscar
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>
> >>>> ---------------------
> >>>>
> >>>>
> >>>>
> >>>>
> >>>> package com.xms.framework.testing.integration.spectransformers;
> >>>>
> >>>> import java.util.HashMap;
> >>>> import java.util.Map;
> >>>> import java.util.regex.Matcher;
> >>>> import java.util.regex.Pattern;
> >>>>
> >>>> import javax.jdo.Query;
> >>>>
> >>>> import org.opensaml.artifact.InvalidArgumentException;
> >>>>
> >>>> import org.apache.isis.applib.DomainObjectContainer;
> >>>> import org.apache.isis.core.metamodel.spec.ObjectSpecification;
> >>>> import org.apache.isis.core.runtime.system.context.IsisContext;
> >>>> import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
> >>>> import
> >>>>
> >>
> org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl;
> >>>>
> >>>> /**
> >>>> * Requires the Gherkin's capture in the format '([entitySingularName]
> >>>> [(.+?)
> >>>> * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'.
> >>>> * <p>
> >>>> * For example:
> >>>> * <ul>
> >>>> * <li>the employee with name "PETER"</li>
> >>>> * <li>the employee named "PETER"</li>
> >>>> * <li>the property with reference "REF-001"</li>
> >>>> * <li>the property referenced "REF-001"</li>
> >>>> * <li>the product with id "PR-001"</li>
> >>>> * </ul>
> >>>> * <p>
> >>>> * For matching the first one we will need the following Gherkin
> regular
> >>>> * expression:
> >>>> * <ul>
> >>>> * <li>
> >>>> * \\@When("The company's (employee with name \"[^\"]*\") has a role
> >>>> assigned")</li>
> >>>> * </ul>
> >>>> * <p>
> >>>> * From there, we know that:
> >>>> * <ul>
> >>>> * <li>The Entity singular name is "employee".</li>
> >>>> * <li>We must search by name</li>
> >>>> * <li>The name must be PETER</li>
> >>>> * </ul>
> >>>> *
> >>>> */
> >>>> public class GenericIsisJdoTransformer extends
> >>>> NullRecognizingTransformer<Object> {
> >>>>
> >>>>   private static Map<String, ObjectSpecification>
> >>>> specificationsBySingularName;
> >>>>
> >>>>   /**
> >>>>    * Tries to obtain the Entity class name, id and search field from
> >> the
> >>>>    * Cucumber capture, and find it on the JDO Object Store.
> >>>>    *
> >>>>    * @see com.xms.framework.testing.integration.spectransformers.
> >>>>    *
>  NullRecognizingTransformer#transformNonNull(java.lang.String)
> >>>>    */
> >>>>   @Override
> >>>>   protected Object transformNonNull(final String capture) {
> >>>>
> >>>>       return this.findFromCapture(capture);
> >>>>
> >>>>   }
> >>>>
> >>>>   /**
> >>>>    * @param entityName
> >>>>    *            Name of the Entity specified on the Gherkin's capture.
> >>>>    * @return
> >>>>    */
> >>>>   private ObjectSpecification specificationOf(final String entityName)
> >> {
> >>>>
> >>>>       if (IsisJdoTransformer.specificationsBySingularName == null) {
> >>>>           IsisJdoTransformer.specificationsBySingularName = new
> >>>> HashMap<String, ObjectSpecification>();
> >>>>
> >>>>           for (final ObjectSpecification current :
> >>>> IsisContext.getSpecificationLoader().allSpecifications()) {
> >>>>
> >>>>
> >>>>
> >>
> IsisJdoTransformer.specificationsBySingularName.put(current.getSingularName().toLowerCase(),
> >>>> current);
> >>>>           }
> >>>>
> >>>>       }
> >>>>       return
> >>>>
> >>
> IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCase());
> >>>>
> >>>>   }
> >>>>
> >>>>   private final Class<?> entityClassFrom(final String capture) {
> >>>>
> >>>>       // The Entity Id will be between "".
> >>>>       String entityName = "";
> >>>>
> >>>>       final String[] recognizedPatterns = { "( with name )", "( named
> >>>> )", "( with reference )", "( referenced )", "( with id )" };
> >>>>
> >>>>       for (final String currentPattern : recognizedPatterns) {
> >>>>           final Pattern p = Pattern.compile(currentPattern);
> >>>>           final Matcher m = p.matcher(capture);
> >>>>           if (m.find()) {
> >>>>               entityName = capture.substring(0, m.start());
> >>>>               break;
> >>>>           }
> >>>>       }
> >>>>
> >>>>       if (entityName == "") {
> >>>>           throw new InvalidArgumentException(String.format("Cannot
> find
> >>>> the entity's name on the capture %s. The format must be
> >>>> '([entitySingularName] [with name|with reference|named|referenced]
> >>>> \"[entityId]\")'", capture));
> >>>>       }
> >>>>
> >>>>       final ObjectSpecification specification =
> >>>> this.specificationOf(entityName);
> >>>>       if (specification == null) {
> >>>>           throw new InvalidArgumentException(String.format("There is
> no
> >>>> Entity registered in Isis with '%s' as it's singular name",
> >> entityName));
> >>>>       }
> >>>>       return specification.getCorrespondingClass();
> >>>>
> >>>>   }
> >>>>
> >>>>   /**
> >>>>    * @param capture
> >>>>    *            The Gherkin's capture
> >>>>    * @return
> >>>>    */
> >>>>   private final String filterFrom(final String capture) {
> >>>>       // Find by "name".
> >>>>       if (capture.matches("(.+?)name(.+?)")) {
> >>>>           return String.format("name==\"%s\"",
> >>>> this.entityIdFrom(capture));
> >>>>       }
> >>>>
> >>>>       // Find by "reference".
> >>>>       if (capture.matches("(.+?)reference(.+?)")) {
> >>>>           return String.format("reference==\"%s\"",
> >>>> this.entityIdFrom(capture));
> >>>>       }
> >>>>
> >>>>       // Find by "id".
> >>>>       if (capture.matches("(.+?)id(.+?)")) {
> >>>>           return String.format("id==\"%s\"",
> >> this.entityIdFrom(capture));
> >>>>       }
> >>>>
> >>>>       throw new InvalidArgumentException(String.format("The entity id
> >>>> has not been found on the capture '%s'. It must be between two \"
> >>>> characters.", capture));
> >>>>   }
> >>>>
> >>>>   private final Object entityIdFrom(final String capture) {
> >>>>       // The Entity Id will be between "".
> >>>>       final Pattern p = Pattern.compile("\"(.+?)\"");
> >>>>       final Matcher m = p.matcher(capture);
> >>>>       if (m.find()) {
> >>>>           return m.group().replace("\"", "");
> >>>>       } else {
> >>>>           throw new InvalidArgumentException(String.format("The entity
> >>>> id has not been found on the capture '%s'. It must be between two \"
> >>>> characters.", capture));
> >>>>       }
> >>>>   }
> >>>>
> >>>>   private final Object findFromCapture(final String capture) {
> >>>>
> >>>>       // Need to flush().
> >>>>       // The Entity can be created on the same transaction.
> >>>>       // If we don't flush() the changes, perhaps the entity have not
> >>>> been
> >>>>       // saved to the object store yet, and will not be found.
> >>>>
> >>>>
> >>
> ScenarioExecution.current().service(DomainObjectContainer.class).flush();
> >>>>
> >>>>       final Query query =
> >>>>
> >>
> ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersistenceManager().newQuery();
> >>>>       query.setClass(this.entityClassFrom(capture));
> >>>>       query.setFilter(this.filterFrom(capture));
> >>>>       query.setUnique(true);
> >>>>
> >>>>       final Object result = query.execute();
> >>>>
> >>>>       return result;
> >>>>
> >>>>   }
> >>>> }
> >>>>
> >>>>
> >>>>
> >>>>
> >>>> ---------------------
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>
> >>>> El 27/08/2013, a las 20:24, Kevin Meyer - KMZ <ke...@kmz.co.za>
> >> escribió:
> >>>>
> >>>>> Hi Jeremy,
> >>>>>
> >>>>> If I remember correctly, Dan has examples for overriding the "Finder"
> >>>>> methods in the ToDo demo / artefact that use JDO search classes to
> >>>>> build queries.
> >>>>>
> >>>>> In fact:
> >>>>>
> >>>>> In the  "ToDoItemsJdo" class (which extends an
> >>>>> AbstractFactoryAndRepository):
> >>>>>  // {{ notYetComplete (action)
> >>>>>  @Override
> >>>>>  protected List<ToDoItem> doNotYetComplete() {
> >>>>>      return allMatches(
> >>>>>              new QueryDefault<ToDoItem>(ToDoItem.class,
> >>>>>                      "todo_notYetComplete",
> >>>>>                      "ownedBy", currentUserName()));
> >>>>>  }
> >>>>>  // }}
> >>>>>
> >>>>> This appears in an old copy of the repo I have in the wicket JDO
> >>>>> quickstart project.
> >>>>>
> >>>>> So while you can't use "FindByPattern", JDO has implemented a
> >>>>> useful alternative.
> >>>>>
> >>>>> Regards,
> >>>>> Kevin
> >>>>>
> >>>>>
> >>>>> On 27 Aug 2013 at 14:37, Jeremy Gurr wrote:
> >>>>>
> >>>>>> I'm playing around with the cucumber support tools in isis (a
> testing
> >>>> framework for behavior driven development, for those who don't know),
> >> and
> >>>> have created a test that basically looks like this:
> >>>>>>
> >>>>>> <snip>
> >>>>>>
> >>>>>> It's very convenient that cucumber instantiates my ServiceClass
> model
> >>>> object and automatically plugs in fields according to the feature spec
> >>>> column header. This enables me to add new fields simply by adding a
> >> column
> >>>> in the spec, and adding the corresponding column in my model object.
> It
> >>>> skips the extra hassle of having to update the glue code as well as
> >> service
> >>>> methods to construct the object.
> >>>>>>
> >>>>>> The "exists" method contains this code:
> >>>>>>
> >>>>>> public boolean exists(ServiceClass serviceClass) {
> >>>>>> final QueryFindByPattern<ServiceClass> query = new
> >>>> QueryFindByPattern<ServiceClass>(ServiceClass.class, serviceClass);
> >>>>>> final ServiceClass firstMatch = firstMatch(query);
> >>>>>>
> >>>>>> return firstMatch != null;
> >>>>>> }
> >>>>>>
> >>>>>> I'm just trying to verify that an object exists with fields matching
> >>>> the incoming object, some fields of which may be null, meaning that
> they
> >>>> can be any value. The QueryFindByPattern class seemed to be a perfect
> >> fit
> >>>> since I'm passing around ServiceClass instances anyway. However,
> >> running it
> >>>> executes code that is not yet implemented:
> >>>>>>
> >>>>>> <snip>
> >>>>
> >>>>
> >>
>
>

Re: Using JDO helper methods to check existence of an object for a test

Posted by GESCONSULTOR - Óscar Bou <o....@gesconsultor.com>.
That sounds really nice...



Are stress tests also included, in addition to regression tests? 

That raised on the list the previous weeks.  It will be really interesting to know if there are any concurrency issues at Wicket, Isis or DN. 

Seems that Selenium can also be used in a grid configuration to simulate stress load tests.

The creator of Selenium has founded also a company specialized on web stress tests [1]. As it's integrated with Amazon Web Services, you can configure your stress environment (and pay for it). 

I've just registered a trial account right now and seems good.

[1] https://home.wpm.neustar.biz/



El 08/09/2013, a las 09:45, Dan Haywood <da...@haywood-associates.co.uk> escribió:

> That's fine with me.
> 
> This week I'm starting to look at automated web browser tests (possibly
> using Geb [1] as a layer over Selenium).  Plan is for Estatio to act as a
> regression suite for Isis as a whole.  So no immediate rush my end.
> 
> Cheers
> Dan
> 
> 
> [1] http://www.gebish.org/
> 
> 
> On 8 September 2013 08:35, GESCONSULTOR <o....@gesconsultor.com> wrote:
> 
>> For sure!
>> 
>> Unless you or Jeremy plan to use it immediately let me check and improve
>> it this week through some BDD features we must implement.
>> 
>> Thanks,
>> 
>> Oscar
>> 
>> 
>> El 08/09/2013, a las 09:20, Dan Haywood <da...@haywood-associates.co.uk>
>> escribió:
>> 
>>> Looks very good, Oscar, exactly the sort of thing that should be in the
>>> framework, I think.
>>> 
>>> Could you raise a ticket and provide a commit?  If you have the time,
>>> perhaps you could also refactor the ToDo app to show its use?
>>> 
>>> Many thanks
>>> 
>>> Cheers
>>> Dan
>>> 
>>> 
>>> 
>>> On 7 September 2013 19:05, GESCONSULTOR - Óscar Bou
>>> <o....@gesconsultor.com>wrote:
>>> 
>>>> 
>>>> I've just been thinking about a way to avoid to define a Spec
>> Transformer
>>>> for every Entity class in the Domain.
>>>> 
>>>> As it's just "infrastructure software", I thought that was "just
>> enough".
>>>> 
>>>> The attached code allows to use just one Spec Transformer when the Glue
>> is
>>>> written like this:
>>>> -  the employee with name "PETER"
>>>> - the employee named "PETER"
>>>> - the property with reference "REF-001"
>>>> - the property referenced "REF-001"
>>>> - the product with id "PR-001"
>>>> 
>>>> From there, we know that:
>>>> - The Entity singular name is "employee" (so it can be obtained from the
>>>> Isis Object Specifications, reinforcing the Ubiquitous Language use on
>> BDD
>>>> specifications).
>>>> - We must search by name
>>>> - The name must be PETER
>>>> 
>>>> 
>>>> 
>>>> For using it, I must define a Glue like this one:
>>>> 
>>>> Define a Glue like this one:
>>>> @When("The company (employee with name \"[^\"]*\") has a role assigned")
>>>> public void
>>>> 
>> the_company_employee_with_name(@Transform(GenericIsisJdoTransformer.class)
>>>> final  Employee employee) {
>>>>  ...
>>>> }
>>>> 
>>>> 
>>>> 
>>>> First "proof-of-concept" code version follows this text.
>>>> 
>>>> It can be improved in many ways, for allowing customization through
>>>> inheritance, avoiding JDO through the use of the Apache Isis query
>> methods,
>>>> etc.
>>>> 
>>>> 
>>>> But I would let you know, right to know if you think it can be useful.
>>>> 
>>>> On this way, the only implemented classes  are the ones supporting the
>>>> Glues (and only the Spec Transformers for more specific use cases).
>>>> 
>>>> 
>>>> HTH,
>>>> 
>>>> Oscar
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> ---------------------
>>>> 
>>>> 
>>>> 
>>>> 
>>>> package com.xms.framework.testing.integration.spectransformers;
>>>> 
>>>> import java.util.HashMap;
>>>> import java.util.Map;
>>>> import java.util.regex.Matcher;
>>>> import java.util.regex.Pattern;
>>>> 
>>>> import javax.jdo.Query;
>>>> 
>>>> import org.opensaml.artifact.InvalidArgumentException;
>>>> 
>>>> import org.apache.isis.applib.DomainObjectContainer;
>>>> import org.apache.isis.core.metamodel.spec.ObjectSpecification;
>>>> import org.apache.isis.core.runtime.system.context.IsisContext;
>>>> import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
>>>> import
>>>> 
>> org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl;
>>>> 
>>>> /**
>>>> * Requires the Gherkin's capture in the format '([entitySingularName]
>>>> [(.+?)
>>>> * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'.
>>>> * <p>
>>>> * For example:
>>>> * <ul>
>>>> * <li>the employee with name "PETER"</li>
>>>> * <li>the employee named "PETER"</li>
>>>> * <li>the property with reference "REF-001"</li>
>>>> * <li>the property referenced "REF-001"</li>
>>>> * <li>the product with id "PR-001"</li>
>>>> * </ul>
>>>> * <p>
>>>> * For matching the first one we will need the following Gherkin regular
>>>> * expression:
>>>> * <ul>
>>>> * <li>
>>>> * \\@When("The company's (employee with name \"[^\"]*\") has a role
>>>> assigned")</li>
>>>> * </ul>
>>>> * <p>
>>>> * From there, we know that:
>>>> * <ul>
>>>> * <li>The Entity singular name is "employee".</li>
>>>> * <li>We must search by name</li>
>>>> * <li>The name must be PETER</li>
>>>> * </ul>
>>>> *
>>>> */
>>>> public class GenericIsisJdoTransformer extends
>>>> NullRecognizingTransformer<Object> {
>>>> 
>>>>   private static Map<String, ObjectSpecification>
>>>> specificationsBySingularName;
>>>> 
>>>>   /**
>>>>    * Tries to obtain the Entity class name, id and search field from
>> the
>>>>    * Cucumber capture, and find it on the JDO Object Store.
>>>>    *
>>>>    * @see com.xms.framework.testing.integration.spectransformers.
>>>>    *      NullRecognizingTransformer#transformNonNull(java.lang.String)
>>>>    */
>>>>   @Override
>>>>   protected Object transformNonNull(final String capture) {
>>>> 
>>>>       return this.findFromCapture(capture);
>>>> 
>>>>   }
>>>> 
>>>>   /**
>>>>    * @param entityName
>>>>    *            Name of the Entity specified on the Gherkin's capture.
>>>>    * @return
>>>>    */
>>>>   private ObjectSpecification specificationOf(final String entityName)
>> {
>>>> 
>>>>       if (IsisJdoTransformer.specificationsBySingularName == null) {
>>>>           IsisJdoTransformer.specificationsBySingularName = new
>>>> HashMap<String, ObjectSpecification>();
>>>> 
>>>>           for (final ObjectSpecification current :
>>>> IsisContext.getSpecificationLoader().allSpecifications()) {
>>>> 
>>>> 
>>>> 
>> IsisJdoTransformer.specificationsBySingularName.put(current.getSingularName().toLowerCase(),
>>>> current);
>>>>           }
>>>> 
>>>>       }
>>>>       return
>>>> 
>> IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCase());
>>>> 
>>>>   }
>>>> 
>>>>   private final Class<?> entityClassFrom(final String capture) {
>>>> 
>>>>       // The Entity Id will be between "".
>>>>       String entityName = "";
>>>> 
>>>>       final String[] recognizedPatterns = { "( with name )", "( named
>>>> )", "( with reference )", "( referenced )", "( with id )" };
>>>> 
>>>>       for (final String currentPattern : recognizedPatterns) {
>>>>           final Pattern p = Pattern.compile(currentPattern);
>>>>           final Matcher m = p.matcher(capture);
>>>>           if (m.find()) {
>>>>               entityName = capture.substring(0, m.start());
>>>>               break;
>>>>           }
>>>>       }
>>>> 
>>>>       if (entityName == "") {
>>>>           throw new InvalidArgumentException(String.format("Cannot find
>>>> the entity's name on the capture %s. The format must be
>>>> '([entitySingularName] [with name|with reference|named|referenced]
>>>> \"[entityId]\")'", capture));
>>>>       }
>>>> 
>>>>       final ObjectSpecification specification =
>>>> this.specificationOf(entityName);
>>>>       if (specification == null) {
>>>>           throw new InvalidArgumentException(String.format("There is no
>>>> Entity registered in Isis with '%s' as it's singular name",
>> entityName));
>>>>       }
>>>>       return specification.getCorrespondingClass();
>>>> 
>>>>   }
>>>> 
>>>>   /**
>>>>    * @param capture
>>>>    *            The Gherkin's capture
>>>>    * @return
>>>>    */
>>>>   private final String filterFrom(final String capture) {
>>>>       // Find by "name".
>>>>       if (capture.matches("(.+?)name(.+?)")) {
>>>>           return String.format("name==\"%s\"",
>>>> this.entityIdFrom(capture));
>>>>       }
>>>> 
>>>>       // Find by "reference".
>>>>       if (capture.matches("(.+?)reference(.+?)")) {
>>>>           return String.format("reference==\"%s\"",
>>>> this.entityIdFrom(capture));
>>>>       }
>>>> 
>>>>       // Find by "id".
>>>>       if (capture.matches("(.+?)id(.+?)")) {
>>>>           return String.format("id==\"%s\"",
>> this.entityIdFrom(capture));
>>>>       }
>>>> 
>>>>       throw new InvalidArgumentException(String.format("The entity id
>>>> has not been found on the capture '%s'. It must be between two \"
>>>> characters.", capture));
>>>>   }
>>>> 
>>>>   private final Object entityIdFrom(final String capture) {
>>>>       // The Entity Id will be between "".
>>>>       final Pattern p = Pattern.compile("\"(.+?)\"");
>>>>       final Matcher m = p.matcher(capture);
>>>>       if (m.find()) {
>>>>           return m.group().replace("\"", "");
>>>>       } else {
>>>>           throw new InvalidArgumentException(String.format("The entity
>>>> id has not been found on the capture '%s'. It must be between two \"
>>>> characters.", capture));
>>>>       }
>>>>   }
>>>> 
>>>>   private final Object findFromCapture(final String capture) {
>>>> 
>>>>       // Need to flush().
>>>>       // The Entity can be created on the same transaction.
>>>>       // If we don't flush() the changes, perhaps the entity have not
>>>> been
>>>>       // saved to the object store yet, and will not be found.
>>>> 
>>>> 
>> ScenarioExecution.current().service(DomainObjectContainer.class).flush();
>>>> 
>>>>       final Query query =
>>>> 
>> ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersistenceManager().newQuery();
>>>>       query.setClass(this.entityClassFrom(capture));
>>>>       query.setFilter(this.filterFrom(capture));
>>>>       query.setUnique(true);
>>>> 
>>>>       final Object result = query.execute();
>>>> 
>>>>       return result;
>>>> 
>>>>   }
>>>> }
>>>> 
>>>> 
>>>> 
>>>> 
>>>> ---------------------
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> El 27/08/2013, a las 20:24, Kevin Meyer - KMZ <ke...@kmz.co.za>
>> escribió:
>>>> 
>>>>> Hi Jeremy,
>>>>> 
>>>>> If I remember correctly, Dan has examples for overriding the "Finder"
>>>>> methods in the ToDo demo / artefact that use JDO search classes to
>>>>> build queries.
>>>>> 
>>>>> In fact:
>>>>> 
>>>>> In the  "ToDoItemsJdo" class (which extends an
>>>>> AbstractFactoryAndRepository):
>>>>>  // {{ notYetComplete (action)
>>>>>  @Override
>>>>>  protected List<ToDoItem> doNotYetComplete() {
>>>>>      return allMatches(
>>>>>              new QueryDefault<ToDoItem>(ToDoItem.class,
>>>>>                      "todo_notYetComplete",
>>>>>                      "ownedBy", currentUserName()));
>>>>>  }
>>>>>  // }}
>>>>> 
>>>>> This appears in an old copy of the repo I have in the wicket JDO
>>>>> quickstart project.
>>>>> 
>>>>> So while you can't use "FindByPattern", JDO has implemented a
>>>>> useful alternative.
>>>>> 
>>>>> Regards,
>>>>> Kevin
>>>>> 
>>>>> 
>>>>> On 27 Aug 2013 at 14:37, Jeremy Gurr wrote:
>>>>> 
>>>>>> I'm playing around with the cucumber support tools in isis (a testing
>>>> framework for behavior driven development, for those who don't know),
>> and
>>>> have created a test that basically looks like this:
>>>>>> 
>>>>>> <snip>
>>>>>> 
>>>>>> It's very convenient that cucumber instantiates my ServiceClass model
>>>> object and automatically plugs in fields according to the feature spec
>>>> column header. This enables me to add new fields simply by adding a
>> column
>>>> in the spec, and adding the corresponding column in my model object. It
>>>> skips the extra hassle of having to update the glue code as well as
>> service
>>>> methods to construct the object.
>>>>>> 
>>>>>> The "exists" method contains this code:
>>>>>> 
>>>>>> public boolean exists(ServiceClass serviceClass) {
>>>>>> final QueryFindByPattern<ServiceClass> query = new
>>>> QueryFindByPattern<ServiceClass>(ServiceClass.class, serviceClass);
>>>>>> final ServiceClass firstMatch = firstMatch(query);
>>>>>> 
>>>>>> return firstMatch != null;
>>>>>> }
>>>>>> 
>>>>>> I'm just trying to verify that an object exists with fields matching
>>>> the incoming object, some fields of which may be null, meaning that they
>>>> can be any value. The QueryFindByPattern class seemed to be a perfect
>> fit
>>>> since I'm passing around ServiceClass instances anyway. However,
>> running it
>>>> executes code that is not yet implemented:
>>>>>> 
>>>>>> <snip>
>>>> 
>>>> 
>> 


Re: Using JDO helper methods to check existence of an object for a test

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
That's fine with me.

This week I'm starting to look at automated web browser tests (possibly
using Geb [1] as a layer over Selenium).  Plan is for Estatio to act as a
regression suite for Isis as a whole.  So no immediate rush my end.

Cheers
Dan


[1] http://www.gebish.org/


On 8 September 2013 08:35, GESCONSULTOR <o....@gesconsultor.com> wrote:

> For sure!
>
> Unless you or Jeremy plan to use it immediately let me check and improve
> it this week through some BDD features we must implement.
>
> Thanks,
>
> Oscar
>
>
> El 08/09/2013, a las 09:20, Dan Haywood <da...@haywood-associates.co.uk>
> escribió:
>
> > Looks very good, Oscar, exactly the sort of thing that should be in the
> > framework, I think.
> >
> > Could you raise a ticket and provide a commit?  If you have the time,
> > perhaps you could also refactor the ToDo app to show its use?
> >
> > Many thanks
> >
> > Cheers
> > Dan
> >
> >
> >
> > On 7 September 2013 19:05, GESCONSULTOR - Óscar Bou
> > <o....@gesconsultor.com>wrote:
> >
> >>
> >> I've just been thinking about a way to avoid to define a Spec
> Transformer
> >> for every Entity class in the Domain.
> >>
> >> As it's just "infrastructure software", I thought that was "just
> enough".
> >>
> >> The attached code allows to use just one Spec Transformer when the Glue
> is
> >> written like this:
> >> -  the employee with name "PETER"
> >> - the employee named "PETER"
> >> - the property with reference "REF-001"
> >> - the property referenced "REF-001"
> >> - the product with id "PR-001"
> >>
> >> From there, we know that:
> >> - The Entity singular name is "employee" (so it can be obtained from the
> >> Isis Object Specifications, reinforcing the Ubiquitous Language use on
> BDD
> >> specifications).
> >> - We must search by name
> >> - The name must be PETER
> >>
> >>
> >>
> >> For using it, I must define a Glue like this one:
> >>
> >> Define a Glue like this one:
> >> @When("The company (employee with name \"[^\"]*\") has a role assigned")
> >> public void
> >>
> the_company_employee_with_name(@Transform(GenericIsisJdoTransformer.class)
> >> final  Employee employee) {
> >>   ...
> >> }
> >>
> >>
> >>
> >> First "proof-of-concept" code version follows this text.
> >>
> >> It can be improved in many ways, for allowing customization through
> >> inheritance, avoiding JDO through the use of the Apache Isis query
> methods,
> >> etc.
> >>
> >>
> >> But I would let you know, right to know if you think it can be useful.
> >>
> >> On this way, the only implemented classes  are the ones supporting the
> >> Glues (and only the Spec Transformers for more specific use cases).
> >>
> >>
> >> HTH,
> >>
> >> Oscar
> >>
> >>
> >>
> >>
> >>
> >> ---------------------
> >>
> >>
> >>
> >>
> >> package com.xms.framework.testing.integration.spectransformers;
> >>
> >> import java.util.HashMap;
> >> import java.util.Map;
> >> import java.util.regex.Matcher;
> >> import java.util.regex.Pattern;
> >>
> >> import javax.jdo.Query;
> >>
> >> import org.opensaml.artifact.InvalidArgumentException;
> >>
> >> import org.apache.isis.applib.DomainObjectContainer;
> >> import org.apache.isis.core.metamodel.spec.ObjectSpecification;
> >> import org.apache.isis.core.runtime.system.context.IsisContext;
> >> import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
> >> import
> >>
> org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl;
> >>
> >> /**
> >> * Requires the Gherkin's capture in the format '([entitySingularName]
> >> [(.+?)
> >> * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'.
> >> * <p>
> >> * For example:
> >> * <ul>
> >> * <li>the employee with name "PETER"</li>
> >> * <li>the employee named "PETER"</li>
> >> * <li>the property with reference "REF-001"</li>
> >> * <li>the property referenced "REF-001"</li>
> >> * <li>the product with id "PR-001"</li>
> >> * </ul>
> >> * <p>
> >> * For matching the first one we will need the following Gherkin regular
> >> * expression:
> >> * <ul>
> >> * <li>
> >> * \\@When("The company's (employee with name \"[^\"]*\") has a role
> >> assigned")</li>
> >> * </ul>
> >> * <p>
> >> * From there, we know that:
> >> * <ul>
> >> * <li>The Entity singular name is "employee".</li>
> >> * <li>We must search by name</li>
> >> * <li>The name must be PETER</li>
> >> * </ul>
> >> *
> >> */
> >> public class GenericIsisJdoTransformer extends
> >> NullRecognizingTransformer<Object> {
> >>
> >>    private static Map<String, ObjectSpecification>
> >> specificationsBySingularName;
> >>
> >>    /**
> >>     * Tries to obtain the Entity class name, id and search field from
> the
> >>     * Cucumber capture, and find it on the JDO Object Store.
> >>     *
> >>     * @see com.xms.framework.testing.integration.spectransformers.
> >>     *      NullRecognizingTransformer#transformNonNull(java.lang.String)
> >>     */
> >>    @Override
> >>    protected Object transformNonNull(final String capture) {
> >>
> >>        return this.findFromCapture(capture);
> >>
> >>    }
> >>
> >>    /**
> >>     * @param entityName
> >>     *            Name of the Entity specified on the Gherkin's capture.
> >>     * @return
> >>     */
> >>    private ObjectSpecification specificationOf(final String entityName)
> {
> >>
> >>        if (IsisJdoTransformer.specificationsBySingularName == null) {
> >>            IsisJdoTransformer.specificationsBySingularName = new
> >> HashMap<String, ObjectSpecification>();
> >>
> >>            for (final ObjectSpecification current :
> >> IsisContext.getSpecificationLoader().allSpecifications()) {
> >>
> >>
> >>
> IsisJdoTransformer.specificationsBySingularName.put(current.getSingularName().toLowerCase(),
> >> current);
> >>            }
> >>
> >>        }
> >>        return
> >>
> IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCase());
> >>
> >>    }
> >>
> >>    private final Class<?> entityClassFrom(final String capture) {
> >>
> >>        // The Entity Id will be between "".
> >>        String entityName = "";
> >>
> >>        final String[] recognizedPatterns = { "( with name )", "( named
> >> )", "( with reference )", "( referenced )", "( with id )" };
> >>
> >>        for (final String currentPattern : recognizedPatterns) {
> >>            final Pattern p = Pattern.compile(currentPattern);
> >>            final Matcher m = p.matcher(capture);
> >>            if (m.find()) {
> >>                entityName = capture.substring(0, m.start());
> >>                break;
> >>            }
> >>        }
> >>
> >>        if (entityName == "") {
> >>            throw new InvalidArgumentException(String.format("Cannot find
> >> the entity's name on the capture %s. The format must be
> >> '([entitySingularName] [with name|with reference|named|referenced]
> >> \"[entityId]\")'", capture));
> >>        }
> >>
> >>        final ObjectSpecification specification =
> >> this.specificationOf(entityName);
> >>        if (specification == null) {
> >>            throw new InvalidArgumentException(String.format("There is no
> >> Entity registered in Isis with '%s' as it's singular name",
> entityName));
> >>        }
> >>        return specification.getCorrespondingClass();
> >>
> >>    }
> >>
> >>    /**
> >>     * @param capture
> >>     *            The Gherkin's capture
> >>     * @return
> >>     */
> >>    private final String filterFrom(final String capture) {
> >>        // Find by "name".
> >>        if (capture.matches("(.+?)name(.+?)")) {
> >>            return String.format("name==\"%s\"",
> >> this.entityIdFrom(capture));
> >>        }
> >>
> >>        // Find by "reference".
> >>        if (capture.matches("(.+?)reference(.+?)")) {
> >>            return String.format("reference==\"%s\"",
> >> this.entityIdFrom(capture));
> >>        }
> >>
> >>        // Find by "id".
> >>        if (capture.matches("(.+?)id(.+?)")) {
> >>            return String.format("id==\"%s\"",
> this.entityIdFrom(capture));
> >>        }
> >>
> >>        throw new InvalidArgumentException(String.format("The entity id
> >> has not been found on the capture '%s'. It must be between two \"
> >> characters.", capture));
> >>    }
> >>
> >>    private final Object entityIdFrom(final String capture) {
> >>        // The Entity Id will be between "".
> >>        final Pattern p = Pattern.compile("\"(.+?)\"");
> >>        final Matcher m = p.matcher(capture);
> >>        if (m.find()) {
> >>            return m.group().replace("\"", "");
> >>        } else {
> >>            throw new InvalidArgumentException(String.format("The entity
> >> id has not been found on the capture '%s'. It must be between two \"
> >> characters.", capture));
> >>        }
> >>    }
> >>
> >>    private final Object findFromCapture(final String capture) {
> >>
> >>        // Need to flush().
> >>        // The Entity can be created on the same transaction.
> >>        // If we don't flush() the changes, perhaps the entity have not
> >> been
> >>        // saved to the object store yet, and will not be found.
> >>
> >>
> ScenarioExecution.current().service(DomainObjectContainer.class).flush();
> >>
> >>        final Query query =
> >>
> ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersistenceManager().newQuery();
> >>        query.setClass(this.entityClassFrom(capture));
> >>        query.setFilter(this.filterFrom(capture));
> >>        query.setUnique(true);
> >>
> >>        final Object result = query.execute();
> >>
> >>        return result;
> >>
> >>    }
> >> }
> >>
> >>
> >>
> >>
> >> ---------------------
> >>
> >>
> >>
> >>
> >>
> >> El 27/08/2013, a las 20:24, Kevin Meyer - KMZ <ke...@kmz.co.za>
> escribió:
> >>
> >>> Hi Jeremy,
> >>>
> >>> If I remember correctly, Dan has examples for overriding the "Finder"
> >>> methods in the ToDo demo / artefact that use JDO search classes to
> >>> build queries.
> >>>
> >>> In fact:
> >>>
> >>> In the  "ToDoItemsJdo" class (which extends an
> >>> AbstractFactoryAndRepository):
> >>>   // {{ notYetComplete (action)
> >>>   @Override
> >>>   protected List<ToDoItem> doNotYetComplete() {
> >>>       return allMatches(
> >>>               new QueryDefault<ToDoItem>(ToDoItem.class,
> >>>                       "todo_notYetComplete",
> >>>                       "ownedBy", currentUserName()));
> >>>   }
> >>>   // }}
> >>>
> >>> This appears in an old copy of the repo I have in the wicket JDO
> >>> quickstart project.
> >>>
> >>> So while you can't use "FindByPattern", JDO has implemented a
> >>> useful alternative.
> >>>
> >>> Regards,
> >>> Kevin
> >>>
> >>>
> >>> On 27 Aug 2013 at 14:37, Jeremy Gurr wrote:
> >>>
> >>>> I'm playing around with the cucumber support tools in isis (a testing
> >> framework for behavior driven development, for those who don't know),
> and
> >> have created a test that basically looks like this:
> >>>>
> >>>> <snip>
> >>>>
> >>>> It's very convenient that cucumber instantiates my ServiceClass model
> >> object and automatically plugs in fields according to the feature spec
> >> column header. This enables me to add new fields simply by adding a
> column
> >> in the spec, and adding the corresponding column in my model object. It
> >> skips the extra hassle of having to update the glue code as well as
> service
> >> methods to construct the object.
> >>>>
> >>>> The "exists" method contains this code:
> >>>>
> >>>> public boolean exists(ServiceClass serviceClass) {
> >>>> final QueryFindByPattern<ServiceClass> query = new
> >> QueryFindByPattern<ServiceClass>(ServiceClass.class, serviceClass);
> >>>> final ServiceClass firstMatch = firstMatch(query);
> >>>>
> >>>> return firstMatch != null;
> >>>> }
> >>>>
> >>>> I'm just trying to verify that an object exists with fields matching
> >> the incoming object, some fields of which may be null, meaning that they
> >> can be any value. The QueryFindByPattern class seemed to be a perfect
> fit
> >> since I'm passing around ServiceClass instances anyway. However,
> running it
> >> executes code that is not yet implemented:
> >>>>
> >>>> <snip>
> >>
> >>
>

Re: Using JDO helper methods to check existence of an object for a test

Posted by GESCONSULTOR <o....@gesconsultor.com>.
For sure!

Unless you or Jeremy plan to use it immediately let me check and improve it this week through some BDD features we must implement.

Thanks,

Oscar


El 08/09/2013, a las 09:20, Dan Haywood <da...@haywood-associates.co.uk> escribió:

> Looks very good, Oscar, exactly the sort of thing that should be in the
> framework, I think.
> 
> Could you raise a ticket and provide a commit?  If you have the time,
> perhaps you could also refactor the ToDo app to show its use?
> 
> Many thanks
> 
> Cheers
> Dan
> 
> 
> 
> On 7 September 2013 19:05, GESCONSULTOR - Óscar Bou
> <o....@gesconsultor.com>wrote:
> 
>> 
>> I've just been thinking about a way to avoid to define a Spec Transformer
>> for every Entity class in the Domain.
>> 
>> As it's just "infrastructure software", I thought that was "just enough".
>> 
>> The attached code allows to use just one Spec Transformer when the Glue is
>> written like this:
>> -  the employee with name "PETER"
>> - the employee named "PETER"
>> - the property with reference "REF-001"
>> - the property referenced "REF-001"
>> - the product with id "PR-001"
>> 
>> From there, we know that:
>> - The Entity singular name is "employee" (so it can be obtained from the
>> Isis Object Specifications, reinforcing the Ubiquitous Language use on BDD
>> specifications).
>> - We must search by name
>> - The name must be PETER
>> 
>> 
>> 
>> For using it, I must define a Glue like this one:
>> 
>> Define a Glue like this one:
>> @When("The company (employee with name \"[^\"]*\") has a role assigned")
>> public void
>> the_company_employee_with_name(@Transform(GenericIsisJdoTransformer.class)
>> final  Employee employee) {
>>   ...
>> }
>> 
>> 
>> 
>> First "proof-of-concept" code version follows this text.
>> 
>> It can be improved in many ways, for allowing customization through
>> inheritance, avoiding JDO through the use of the Apache Isis query methods,
>> etc.
>> 
>> 
>> But I would let you know, right to know if you think it can be useful.
>> 
>> On this way, the only implemented classes  are the ones supporting the
>> Glues (and only the Spec Transformers for more specific use cases).
>> 
>> 
>> HTH,
>> 
>> Oscar
>> 
>> 
>> 
>> 
>> 
>> ---------------------
>> 
>> 
>> 
>> 
>> package com.xms.framework.testing.integration.spectransformers;
>> 
>> import java.util.HashMap;
>> import java.util.Map;
>> import java.util.regex.Matcher;
>> import java.util.regex.Pattern;
>> 
>> import javax.jdo.Query;
>> 
>> import org.opensaml.artifact.InvalidArgumentException;
>> 
>> import org.apache.isis.applib.DomainObjectContainer;
>> import org.apache.isis.core.metamodel.spec.ObjectSpecification;
>> import org.apache.isis.core.runtime.system.context.IsisContext;
>> import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
>> import
>> org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl;
>> 
>> /**
>> * Requires the Gherkin's capture in the format '([entitySingularName]
>> [(.+?)
>> * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'.
>> * <p>
>> * For example:
>> * <ul>
>> * <li>the employee with name "PETER"</li>
>> * <li>the employee named "PETER"</li>
>> * <li>the property with reference "REF-001"</li>
>> * <li>the property referenced "REF-001"</li>
>> * <li>the product with id "PR-001"</li>
>> * </ul>
>> * <p>
>> * For matching the first one we will need the following Gherkin regular
>> * expression:
>> * <ul>
>> * <li>
>> * \\@When("The company's (employee with name \"[^\"]*\") has a role
>> assigned")</li>
>> * </ul>
>> * <p>
>> * From there, we know that:
>> * <ul>
>> * <li>The Entity singular name is "employee".</li>
>> * <li>We must search by name</li>
>> * <li>The name must be PETER</li>
>> * </ul>
>> *
>> */
>> public class GenericIsisJdoTransformer extends
>> NullRecognizingTransformer<Object> {
>> 
>>    private static Map<String, ObjectSpecification>
>> specificationsBySingularName;
>> 
>>    /**
>>     * Tries to obtain the Entity class name, id and search field from the
>>     * Cucumber capture, and find it on the JDO Object Store.
>>     *
>>     * @see com.xms.framework.testing.integration.spectransformers.
>>     *      NullRecognizingTransformer#transformNonNull(java.lang.String)
>>     */
>>    @Override
>>    protected Object transformNonNull(final String capture) {
>> 
>>        return this.findFromCapture(capture);
>> 
>>    }
>> 
>>    /**
>>     * @param entityName
>>     *            Name of the Entity specified on the Gherkin's capture.
>>     * @return
>>     */
>>    private ObjectSpecification specificationOf(final String entityName) {
>> 
>>        if (IsisJdoTransformer.specificationsBySingularName == null) {
>>            IsisJdoTransformer.specificationsBySingularName = new
>> HashMap<String, ObjectSpecification>();
>> 
>>            for (final ObjectSpecification current :
>> IsisContext.getSpecificationLoader().allSpecifications()) {
>> 
>> 
>> IsisJdoTransformer.specificationsBySingularName.put(current.getSingularName().toLowerCase(),
>> current);
>>            }
>> 
>>        }
>>        return
>> IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCase());
>> 
>>    }
>> 
>>    private final Class<?> entityClassFrom(final String capture) {
>> 
>>        // The Entity Id will be between "".
>>        String entityName = "";
>> 
>>        final String[] recognizedPatterns = { "( with name )", "( named
>> )", "( with reference )", "( referenced )", "( with id )" };
>> 
>>        for (final String currentPattern : recognizedPatterns) {
>>            final Pattern p = Pattern.compile(currentPattern);
>>            final Matcher m = p.matcher(capture);
>>            if (m.find()) {
>>                entityName = capture.substring(0, m.start());
>>                break;
>>            }
>>        }
>> 
>>        if (entityName == "") {
>>            throw new InvalidArgumentException(String.format("Cannot find
>> the entity's name on the capture %s. The format must be
>> '([entitySingularName] [with name|with reference|named|referenced]
>> \"[entityId]\")'", capture));
>>        }
>> 
>>        final ObjectSpecification specification =
>> this.specificationOf(entityName);
>>        if (specification == null) {
>>            throw new InvalidArgumentException(String.format("There is no
>> Entity registered in Isis with '%s' as it's singular name", entityName));
>>        }
>>        return specification.getCorrespondingClass();
>> 
>>    }
>> 
>>    /**
>>     * @param capture
>>     *            The Gherkin's capture
>>     * @return
>>     */
>>    private final String filterFrom(final String capture) {
>>        // Find by "name".
>>        if (capture.matches("(.+?)name(.+?)")) {
>>            return String.format("name==\"%s\"",
>> this.entityIdFrom(capture));
>>        }
>> 
>>        // Find by "reference".
>>        if (capture.matches("(.+?)reference(.+?)")) {
>>            return String.format("reference==\"%s\"",
>> this.entityIdFrom(capture));
>>        }
>> 
>>        // Find by "id".
>>        if (capture.matches("(.+?)id(.+?)")) {
>>            return String.format("id==\"%s\"", this.entityIdFrom(capture));
>>        }
>> 
>>        throw new InvalidArgumentException(String.format("The entity id
>> has not been found on the capture '%s'. It must be between two \"
>> characters.", capture));
>>    }
>> 
>>    private final Object entityIdFrom(final String capture) {
>>        // The Entity Id will be between "".
>>        final Pattern p = Pattern.compile("\"(.+?)\"");
>>        final Matcher m = p.matcher(capture);
>>        if (m.find()) {
>>            return m.group().replace("\"", "");
>>        } else {
>>            throw new InvalidArgumentException(String.format("The entity
>> id has not been found on the capture '%s'. It must be between two \"
>> characters.", capture));
>>        }
>>    }
>> 
>>    private final Object findFromCapture(final String capture) {
>> 
>>        // Need to flush().
>>        // The Entity can be created on the same transaction.
>>        // If we don't flush() the changes, perhaps the entity have not
>> been
>>        // saved to the object store yet, and will not be found.
>> 
>> ScenarioExecution.current().service(DomainObjectContainer.class).flush();
>> 
>>        final Query query =
>> ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersistenceManager().newQuery();
>>        query.setClass(this.entityClassFrom(capture));
>>        query.setFilter(this.filterFrom(capture));
>>        query.setUnique(true);
>> 
>>        final Object result = query.execute();
>> 
>>        return result;
>> 
>>    }
>> }
>> 
>> 
>> 
>> 
>> ---------------------
>> 
>> 
>> 
>> 
>> 
>> El 27/08/2013, a las 20:24, Kevin Meyer - KMZ <ke...@kmz.co.za> escribió:
>> 
>>> Hi Jeremy,
>>> 
>>> If I remember correctly, Dan has examples for overriding the "Finder"
>>> methods in the ToDo demo / artefact that use JDO search classes to
>>> build queries.
>>> 
>>> In fact:
>>> 
>>> In the  "ToDoItemsJdo" class (which extends an
>>> AbstractFactoryAndRepository):
>>>   // {{ notYetComplete (action)
>>>   @Override
>>>   protected List<ToDoItem> doNotYetComplete() {
>>>       return allMatches(
>>>               new QueryDefault<ToDoItem>(ToDoItem.class,
>>>                       "todo_notYetComplete",
>>>                       "ownedBy", currentUserName()));
>>>   }
>>>   // }}
>>> 
>>> This appears in an old copy of the repo I have in the wicket JDO
>>> quickstart project.
>>> 
>>> So while you can't use "FindByPattern", JDO has implemented a
>>> useful alternative.
>>> 
>>> Regards,
>>> Kevin
>>> 
>>> 
>>> On 27 Aug 2013 at 14:37, Jeremy Gurr wrote:
>>> 
>>>> I'm playing around with the cucumber support tools in isis (a testing
>> framework for behavior driven development, for those who don't know), and
>> have created a test that basically looks like this:
>>>> 
>>>> <snip>
>>>> 
>>>> It's very convenient that cucumber instantiates my ServiceClass model
>> object and automatically plugs in fields according to the feature spec
>> column header. This enables me to add new fields simply by adding a column
>> in the spec, and adding the corresponding column in my model object. It
>> skips the extra hassle of having to update the glue code as well as service
>> methods to construct the object.
>>>> 
>>>> The "exists" method contains this code:
>>>> 
>>>> public boolean exists(ServiceClass serviceClass) {
>>>> final QueryFindByPattern<ServiceClass> query = new
>> QueryFindByPattern<ServiceClass>(ServiceClass.class, serviceClass);
>>>> final ServiceClass firstMatch = firstMatch(query);
>>>> 
>>>> return firstMatch != null;
>>>> }
>>>> 
>>>> I'm just trying to verify that an object exists with fields matching
>> the incoming object, some fields of which may be null, meaning that they
>> can be any value. The QueryFindByPattern class seemed to be a perfect fit
>> since I'm passing around ServiceClass instances anyway. However, running it
>> executes code that is not yet implemented:
>>>> 
>>>> <snip>
>> 
>> 

Re: Using JDO helper methods to check existence of an object for a test

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
Looks very good, Oscar, exactly the sort of thing that should be in the
framework, I think.

Could you raise a ticket and provide a commit?  If you have the time,
perhaps you could also refactor the ToDo app to show its use?

Many thanks

Cheers
Dan



On 7 September 2013 19:05, GESCONSULTOR - Óscar Bou
<o....@gesconsultor.com>wrote:

>
> I've just been thinking about a way to avoid to define a Spec Transformer
> for every Entity class in the Domain.
>
> As it's just "infrastructure software", I thought that was "just enough".
>
> The attached code allows to use just one Spec Transformer when the Glue is
> written like this:
> -  the employee with name "PETER"
> - the employee named "PETER"
> - the property with reference "REF-001"
> - the property referenced "REF-001"
> - the product with id "PR-001"
>
> From there, we know that:
> - The Entity singular name is "employee" (so it can be obtained from the
> Isis Object Specifications, reinforcing the Ubiquitous Language use on BDD
> specifications).
> - We must search by name
> - The name must be PETER
>
>
>
> For using it, I must define a Glue like this one:
>
> Define a Glue like this one:
> @When("The company (employee with name \"[^\"]*\") has a role assigned")
> public void
> the_company_employee_with_name(@Transform(GenericIsisJdoTransformer.class)
> final  Employee employee) {
>    ...
> }
>
>
>
> First "proof-of-concept" code version follows this text.
>
> It can be improved in many ways, for allowing customization through
> inheritance, avoiding JDO through the use of the Apache Isis query methods,
> etc.
>
>
> But I would let you know, right to know if you think it can be useful.
>
> On this way, the only implemented classes  are the ones supporting the
> Glues (and only the Spec Transformers for more specific use cases).
>
>
> HTH,
>
> Oscar
>
>
>
>
>
> ---------------------
>
>
>
>
> package com.xms.framework.testing.integration.spectransformers;
>
> import java.util.HashMap;
> import java.util.Map;
> import java.util.regex.Matcher;
> import java.util.regex.Pattern;
>
> import javax.jdo.Query;
>
> import org.opensaml.artifact.InvalidArgumentException;
>
> import org.apache.isis.applib.DomainObjectContainer;
> import org.apache.isis.core.metamodel.spec.ObjectSpecification;
> import org.apache.isis.core.runtime.system.context.IsisContext;
> import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
> import
> org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl;
>
> /**
>  * Requires the Gherkin's capture in the format '([entitySingularName]
> [(.+?)
>  * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'.
>  * <p>
>  * For example:
>  * <ul>
>  * <li>the employee with name "PETER"</li>
>  * <li>the employee named "PETER"</li>
>  * <li>the property with reference "REF-001"</li>
>  * <li>the property referenced "REF-001"</li>
>  * <li>the product with id "PR-001"</li>
>  * </ul>
>  * <p>
>  * For matching the first one we will need the following Gherkin regular
>  * expression:
>  * <ul>
>  * <li>
>  * \\@When("The company's (employee with name \"[^\"]*\") has a role
> assigned")</li>
>  * </ul>
>  * <p>
>  * From there, we know that:
>  * <ul>
>  * <li>The Entity singular name is "employee".</li>
>  * <li>We must search by name</li>
>  * <li>The name must be PETER</li>
>  * </ul>
>  *
>  */
> public class GenericIsisJdoTransformer extends
> NullRecognizingTransformer<Object> {
>
>     private static Map<String, ObjectSpecification>
> specificationsBySingularName;
>
>     /**
>      * Tries to obtain the Entity class name, id and search field from the
>      * Cucumber capture, and find it on the JDO Object Store.
>      *
>      * @see com.xms.framework.testing.integration.spectransformers.
>      *      NullRecognizingTransformer#transformNonNull(java.lang.String)
>      */
>     @Override
>     protected Object transformNonNull(final String capture) {
>
>         return this.findFromCapture(capture);
>
>     }
>
>     /**
>      * @param entityName
>      *            Name of the Entity specified on the Gherkin's capture.
>      * @return
>      */
>     private ObjectSpecification specificationOf(final String entityName) {
>
>         if (IsisJdoTransformer.specificationsBySingularName == null) {
>             IsisJdoTransformer.specificationsBySingularName = new
> HashMap<String, ObjectSpecification>();
>
>             for (final ObjectSpecification current :
> IsisContext.getSpecificationLoader().allSpecifications()) {
>
>
> IsisJdoTransformer.specificationsBySingularName.put(current.getSingularName().toLowerCase(),
> current);
>             }
>
>         }
>         return
> IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCase());
>
>     }
>
>     private final Class<?> entityClassFrom(final String capture) {
>
>         // The Entity Id will be between "".
>         String entityName = "";
>
>         final String[] recognizedPatterns = { "( with name )", "( named
> )", "( with reference )", "( referenced )", "( with id )" };
>
>         for (final String currentPattern : recognizedPatterns) {
>             final Pattern p = Pattern.compile(currentPattern);
>             final Matcher m = p.matcher(capture);
>             if (m.find()) {
>                 entityName = capture.substring(0, m.start());
>                 break;
>             }
>         }
>
>         if (entityName == "") {
>             throw new InvalidArgumentException(String.format("Cannot find
> the entity's name on the capture %s. The format must be
> '([entitySingularName] [with name|with reference|named|referenced]
> \"[entityId]\")'", capture));
>         }
>
>         final ObjectSpecification specification =
> this.specificationOf(entityName);
>         if (specification == null) {
>             throw new InvalidArgumentException(String.format("There is no
> Entity registered in Isis with '%s' as it's singular name", entityName));
>         }
>         return specification.getCorrespondingClass();
>
>     }
>
>     /**
>      * @param capture
>      *            The Gherkin's capture
>      * @return
>      */
>     private final String filterFrom(final String capture) {
>         // Find by "name".
>         if (capture.matches("(.+?)name(.+?)")) {
>             return String.format("name==\"%s\"",
> this.entityIdFrom(capture));
>         }
>
>         // Find by "reference".
>         if (capture.matches("(.+?)reference(.+?)")) {
>             return String.format("reference==\"%s\"",
> this.entityIdFrom(capture));
>         }
>
>         // Find by "id".
>         if (capture.matches("(.+?)id(.+?)")) {
>             return String.format("id==\"%s\"", this.entityIdFrom(capture));
>         }
>
>         throw new InvalidArgumentException(String.format("The entity id
> has not been found on the capture '%s'. It must be between two \"
> characters.", capture));
>     }
>
>     private final Object entityIdFrom(final String capture) {
>         // The Entity Id will be between "".
>         final Pattern p = Pattern.compile("\"(.+?)\"");
>         final Matcher m = p.matcher(capture);
>         if (m.find()) {
>             return m.group().replace("\"", "");
>         } else {
>             throw new InvalidArgumentException(String.format("The entity
> id has not been found on the capture '%s'. It must be between two \"
> characters.", capture));
>         }
>     }
>
>     private final Object findFromCapture(final String capture) {
>
>         // Need to flush().
>         // The Entity can be created on the same transaction.
>         // If we don't flush() the changes, perhaps the entity have not
> been
>         // saved to the object store yet, and will not be found.
>
> ScenarioExecution.current().service(DomainObjectContainer.class).flush();
>
>         final Query query =
> ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersistenceManager().newQuery();
>         query.setClass(this.entityClassFrom(capture));
>         query.setFilter(this.filterFrom(capture));
>         query.setUnique(true);
>
>         final Object result = query.execute();
>
>         return result;
>
>     }
> }
>
>
>
>
> ---------------------
>
>
>
>
>
> El 27/08/2013, a las 20:24, Kevin Meyer - KMZ <ke...@kmz.co.za> escribió:
>
> > Hi Jeremy,
> >
> > If I remember correctly, Dan has examples for overriding the "Finder"
> > methods in the ToDo demo / artefact that use JDO search classes to
> > build queries.
> >
> > In fact:
> >
> > In the  "ToDoItemsJdo" class (which extends an
> > AbstractFactoryAndRepository):
> >    // {{ notYetComplete (action)
> >    @Override
> >    protected List<ToDoItem> doNotYetComplete() {
> >        return allMatches(
> >                new QueryDefault<ToDoItem>(ToDoItem.class,
> >                        "todo_notYetComplete",
> >                        "ownedBy", currentUserName()));
> >    }
> >    // }}
> >
> > This appears in an old copy of the repo I have in the wicket JDO
> > quickstart project.
> >
> > So while you can't use "FindByPattern", JDO has implemented a
> > useful alternative.
> >
> > Regards,
> > Kevin
> >
> >
> > On 27 Aug 2013 at 14:37, Jeremy Gurr wrote:
> >
> >> I'm playing around with the cucumber support tools in isis (a testing
> framework for behavior driven development, for those who don't know), and
> have created a test that basically looks like this:
> >>
> >> <snip>
> >>
> >> It's very convenient that cucumber instantiates my ServiceClass model
> object and automatically plugs in fields according to the feature spec
> column header. This enables me to add new fields simply by adding a column
> in the spec, and adding the corresponding column in my model object. It
> skips the extra hassle of having to update the glue code as well as service
> methods to construct the object.
> >>
> >> The "exists" method contains this code:
> >>
> >> public boolean exists(ServiceClass serviceClass) {
> >> final QueryFindByPattern<ServiceClass> query = new
> QueryFindByPattern<ServiceClass>(ServiceClass.class, serviceClass);
> >> final ServiceClass firstMatch = firstMatch(query);
> >>
> >> return firstMatch != null;
> >> }
> >>
> >> I'm just trying to verify that an object exists with fields matching
> the incoming object, some fields of which may be null, meaning that they
> can be any value. The QueryFindByPattern class seemed to be a perfect fit
> since I'm passing around ServiceClass instances anyway. However, running it
> executes code that is not yet implemented:
> >>
> >> <snip>
>
>

Re: Using JDO helper methods to check existence of an object for a test

Posted by Dan Haywood <da...@haywood-associates.co.uk>.
Looks very good, Oscar, exactly the sort of thing that should be in the
framework, I think.

Could you raise a ticket and provide a commit?  If you have the time,
perhaps you could also refactor the ToDo app to show its use?

Many thanks

Cheers
Dan



On 7 September 2013 19:05, GESCONSULTOR - Óscar Bou
<o....@gesconsultor.com>wrote:

>
> I've just been thinking about a way to avoid to define a Spec Transformer
> for every Entity class in the Domain.
>
> As it's just "infrastructure software", I thought that was "just enough".
>
> The attached code allows to use just one Spec Transformer when the Glue is
> written like this:
> -  the employee with name "PETER"
> - the employee named "PETER"
> - the property with reference "REF-001"
> - the property referenced "REF-001"
> - the product with id "PR-001"
>
> From there, we know that:
> - The Entity singular name is "employee" (so it can be obtained from the
> Isis Object Specifications, reinforcing the Ubiquitous Language use on BDD
> specifications).
> - We must search by name
> - The name must be PETER
>
>
>
> For using it, I must define a Glue like this one:
>
> Define a Glue like this one:
> @When("The company (employee with name \"[^\"]*\") has a role assigned")
> public void
> the_company_employee_with_name(@Transform(GenericIsisJdoTransformer.class)
> final  Employee employee) {
>    ...
> }
>
>
>
> First "proof-of-concept" code version follows this text.
>
> It can be improved in many ways, for allowing customization through
> inheritance, avoiding JDO through the use of the Apache Isis query methods,
> etc.
>
>
> But I would let you know, right to know if you think it can be useful.
>
> On this way, the only implemented classes  are the ones supporting the
> Glues (and only the Spec Transformers for more specific use cases).
>
>
> HTH,
>
> Oscar
>
>
>
>
>
> ---------------------
>
>
>
>
> package com.xms.framework.testing.integration.spectransformers;
>
> import java.util.HashMap;
> import java.util.Map;
> import java.util.regex.Matcher;
> import java.util.regex.Pattern;
>
> import javax.jdo.Query;
>
> import org.opensaml.artifact.InvalidArgumentException;
>
> import org.apache.isis.applib.DomainObjectContainer;
> import org.apache.isis.core.metamodel.spec.ObjectSpecification;
> import org.apache.isis.core.runtime.system.context.IsisContext;
> import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
> import
> org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl;
>
> /**
>  * Requires the Gherkin's capture in the format '([entitySingularName]
> [(.+?)
>  * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'.
>  * <p>
>  * For example:
>  * <ul>
>  * <li>the employee with name "PETER"</li>
>  * <li>the employee named "PETER"</li>
>  * <li>the property with reference "REF-001"</li>
>  * <li>the property referenced "REF-001"</li>
>  * <li>the product with id "PR-001"</li>
>  * </ul>
>  * <p>
>  * For matching the first one we will need the following Gherkin regular
>  * expression:
>  * <ul>
>  * <li>
>  * \\@When("The company's (employee with name \"[^\"]*\") has a role
> assigned")</li>
>  * </ul>
>  * <p>
>  * From there, we know that:
>  * <ul>
>  * <li>The Entity singular name is "employee".</li>
>  * <li>We must search by name</li>
>  * <li>The name must be PETER</li>
>  * </ul>
>  *
>  */
> public class GenericIsisJdoTransformer extends
> NullRecognizingTransformer<Object> {
>
>     private static Map<String, ObjectSpecification>
> specificationsBySingularName;
>
>     /**
>      * Tries to obtain the Entity class name, id and search field from the
>      * Cucumber capture, and find it on the JDO Object Store.
>      *
>      * @see com.xms.framework.testing.integration.spectransformers.
>      *      NullRecognizingTransformer#transformNonNull(java.lang.String)
>      */
>     @Override
>     protected Object transformNonNull(final String capture) {
>
>         return this.findFromCapture(capture);
>
>     }
>
>     /**
>      * @param entityName
>      *            Name of the Entity specified on the Gherkin's capture.
>      * @return
>      */
>     private ObjectSpecification specificationOf(final String entityName) {
>
>         if (IsisJdoTransformer.specificationsBySingularName == null) {
>             IsisJdoTransformer.specificationsBySingularName = new
> HashMap<String, ObjectSpecification>();
>
>             for (final ObjectSpecification current :
> IsisContext.getSpecificationLoader().allSpecifications()) {
>
>
> IsisJdoTransformer.specificationsBySingularName.put(current.getSingularName().toLowerCase(),
> current);
>             }
>
>         }
>         return
> IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCase());
>
>     }
>
>     private final Class<?> entityClassFrom(final String capture) {
>
>         // The Entity Id will be between "".
>         String entityName = "";
>
>         final String[] recognizedPatterns = { "( with name )", "( named
> )", "( with reference )", "( referenced )", "( with id )" };
>
>         for (final String currentPattern : recognizedPatterns) {
>             final Pattern p = Pattern.compile(currentPattern);
>             final Matcher m = p.matcher(capture);
>             if (m.find()) {
>                 entityName = capture.substring(0, m.start());
>                 break;
>             }
>         }
>
>         if (entityName == "") {
>             throw new InvalidArgumentException(String.format("Cannot find
> the entity's name on the capture %s. The format must be
> '([entitySingularName] [with name|with reference|named|referenced]
> \"[entityId]\")'", capture));
>         }
>
>         final ObjectSpecification specification =
> this.specificationOf(entityName);
>         if (specification == null) {
>             throw new InvalidArgumentException(String.format("There is no
> Entity registered in Isis with '%s' as it's singular name", entityName));
>         }
>         return specification.getCorrespondingClass();
>
>     }
>
>     /**
>      * @param capture
>      *            The Gherkin's capture
>      * @return
>      */
>     private final String filterFrom(final String capture) {
>         // Find by "name".
>         if (capture.matches("(.+?)name(.+?)")) {
>             return String.format("name==\"%s\"",
> this.entityIdFrom(capture));
>         }
>
>         // Find by "reference".
>         if (capture.matches("(.+?)reference(.+?)")) {
>             return String.format("reference==\"%s\"",
> this.entityIdFrom(capture));
>         }
>
>         // Find by "id".
>         if (capture.matches("(.+?)id(.+?)")) {
>             return String.format("id==\"%s\"", this.entityIdFrom(capture));
>         }
>
>         throw new InvalidArgumentException(String.format("The entity id
> has not been found on the capture '%s'. It must be between two \"
> characters.", capture));
>     }
>
>     private final Object entityIdFrom(final String capture) {
>         // The Entity Id will be between "".
>         final Pattern p = Pattern.compile("\"(.+?)\"");
>         final Matcher m = p.matcher(capture);
>         if (m.find()) {
>             return m.group().replace("\"", "");
>         } else {
>             throw new InvalidArgumentException(String.format("The entity
> id has not been found on the capture '%s'. It must be between two \"
> characters.", capture));
>         }
>     }
>
>     private final Object findFromCapture(final String capture) {
>
>         // Need to flush().
>         // The Entity can be created on the same transaction.
>         // If we don't flush() the changes, perhaps the entity have not
> been
>         // saved to the object store yet, and will not be found.
>
> ScenarioExecution.current().service(DomainObjectContainer.class).flush();
>
>         final Query query =
> ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersistenceManager().newQuery();
>         query.setClass(this.entityClassFrom(capture));
>         query.setFilter(this.filterFrom(capture));
>         query.setUnique(true);
>
>         final Object result = query.execute();
>
>         return result;
>
>     }
> }
>
>
>
>
> ---------------------
>
>
>
>
>
> El 27/08/2013, a las 20:24, Kevin Meyer - KMZ <ke...@kmz.co.za> escribió:
>
> > Hi Jeremy,
> >
> > If I remember correctly, Dan has examples for overriding the "Finder"
> > methods in the ToDo demo / artefact that use JDO search classes to
> > build queries.
> >
> > In fact:
> >
> > In the  "ToDoItemsJdo" class (which extends an
> > AbstractFactoryAndRepository):
> >    // {{ notYetComplete (action)
> >    @Override
> >    protected List<ToDoItem> doNotYetComplete() {
> >        return allMatches(
> >                new QueryDefault<ToDoItem>(ToDoItem.class,
> >                        "todo_notYetComplete",
> >                        "ownedBy", currentUserName()));
> >    }
> >    // }}
> >
> > This appears in an old copy of the repo I have in the wicket JDO
> > quickstart project.
> >
> > So while you can't use "FindByPattern", JDO has implemented a
> > useful alternative.
> >
> > Regards,
> > Kevin
> >
> >
> > On 27 Aug 2013 at 14:37, Jeremy Gurr wrote:
> >
> >> I'm playing around with the cucumber support tools in isis (a testing
> framework for behavior driven development, for those who don't know), and
> have created a test that basically looks like this:
> >>
> >> <snip>
> >>
> >> It's very convenient that cucumber instantiates my ServiceClass model
> object and automatically plugs in fields according to the feature spec
> column header. This enables me to add new fields simply by adding a column
> in the spec, and adding the corresponding column in my model object. It
> skips the extra hassle of having to update the glue code as well as service
> methods to construct the object.
> >>
> >> The "exists" method contains this code:
> >>
> >> public boolean exists(ServiceClass serviceClass) {
> >> final QueryFindByPattern<ServiceClass> query = new
> QueryFindByPattern<ServiceClass>(ServiceClass.class, serviceClass);
> >> final ServiceClass firstMatch = firstMatch(query);
> >>
> >> return firstMatch != null;
> >> }
> >>
> >> I'm just trying to verify that an object exists with fields matching
> the incoming object, some fields of which may be null, meaning that they
> can be any value. The QueryFindByPattern class seemed to be a perfect fit
> since I'm passing around ServiceClass instances anyway. However, running it
> executes code that is not yet implemented:
> >>
> >> <snip>
>
>