atlanmod / Mogwai

Automatic translation from OCL to Gremlin
10 stars 6 forks source link

How to transform ATL script to Gremlin? #50

Closed ghost closed 5 years ago

ghost commented 5 years ago

Hi I'm trying to generate the Gremlin script/query from an ATL transformation and then run the generated query. I do so as I don't want to persist the result of the transformation and for an java ATLQuery only java MogwaiResource.transform() and java MogwaiResource.process() are supported (at least, MogwaiResource.query() did not work).

I'm using the following code to transform the ATL transformation to Gremlin script:

private static void queryATL2Gremlin2(MogwaiResource mogwaiResource, ATLQuery query) {
    ATL2Gremlin atl2Gremlin = new ATL2Gremlin();

    Module atlModule = (Module) query.getATLResource().getContents().get(0);
    String sourceMMName = atlModule.getInModels().get(0).getMetamodel().getName();
    String targetMMName = atlModule.getOutModels().get(0).getMetamodel().getName();
    Resource gremlinResource = atl2Gremlin.transform(query.getATLResource(), sourceMMName, query.getSourcePackage(), targetMMName, query.getTargetPackage());
    GremlinScript script = ((GremlinScript) gremlinResource.getContents().get(0));
    System.out.println(script);

    GremlinQueryBuilder builder = GremlinQueryBuilder.newBuilder();
    GremlinQuery gQuery =  (GremlinQuery)builder.fromString(script.toString()).build();

    QueryResult result = mogwaiResource.query(gQuery);

    System.out.println(result);
}

Unfortunately, the following exception is raised:

Exception in thread "main" java.lang.RuntimeException: javax.script.ScriptException: groovy.lang.MissingPropertyException: No such property: tHelper for class: Script2
    at fr.inria.atlanmod.mogwai.processor.GremlinScriptRunner.runGremlinScript(GremlinScriptRunner.java:133)
    at fr.inria.atlanmod.mogwai.processor.AbstractQueryProcessor.runGremlinScript(AbstractQueryProcessor.java:374)
    at fr.inria.atlanmod.mogwai.processor.AbstractQueryProcessor.process(AbstractQueryProcessor.java:199)
    at fr.inria.atlanmod.mogwai.processor.GremlinQueryProcessor.process(GremlinQueryProcessor.java:56)
    at fr.inria.atlanmod.mogwai.neoemf.processor.NeoEMFGremlinQueryProcessor.process(NeoEMFGremlinQueryProcessor.java:78)
    at fr.inria.atlanmod.mogwai.neoemf.processor.NeoEMFGremlinQueryProcessor.process(NeoEMFGremlinQueryProcessor.java:1)
    at fr.inria.atlanmod.mogwai.processor.GremlinQueryProcessor.process(GremlinQueryProcessor.java:1)
    at fr.inria.atlanmod.mogwai.query.MogwaiQuery.process(MogwaiQuery.java:99)
    at fr.inria.atlanmod.mogwai.query.MogwaiQuery.process(MogwaiQuery.java:74)
    at fr.inria.atlanmod.mogwai.neoemf.util.NeoEMFQueryHandler.query(NeoEMFQueryHandler.java:177)
    at fr.inria.atlanmod.mogwai.neoemf.resource.DefaultMogwaiResource.query(DefaultMogwaiResource.java:100)
    at fr.inria.atlanmod.mogwai.neoemf.resource.MogwaiResource.query(MogwaiResource.java:122)
    at run.Transformation.queryATL2Gremlin2(Transformation.java:190)
    at run.Transformation.main(Transformation.java:103)
Caused by: javax.script.ScriptException: groovy.lang.MissingPropertyException: No such property: tHelper for class: Script2
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:333)
    at org.codehaus.groovy.jsr223.GroovyCompiledScript.eval(GroovyCompiledScript.java:41)
    at javax.script.CompiledScript.eval(CompiledScript.java:92)
    at fr.inria.atlanmod.mogwai.processor.GremlinScriptRunner.runGremlinScript(GremlinScriptRunner.java:121)
    ... 13 more
Caused by: groovy.lang.MissingPropertyException: No such property: tHelper for class: Script2
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:51)
    at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:49)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:295)
    at Script2.run(Script2.groovy:1)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:330)
    ... 16 more

Then, I tried to initialize Gremlin with other init script available in the project without success (similar problem) :

    private static void queryATL2Gremlin(MogwaiResource mogwaiResource, ATLQuery query, Map<String, Object> qOptions) {
        ATL2Gremlin atl2Gremlin = new ATL2Gremlin();
        atl2Gremlin.enableATLDebug();

        Module atlModule = (Module) query.getATLResource().getContents().get(0);
        String sourceMMName = atlModule.getInModels().get(0).getMetamodel().getName();
        String targetMMName = atlModule.getOutModels().get(0).getMetamodel().getName();
        Resource gremlinResource = atl2Gremlin.transform(query.getATLResource(), sourceMMName, query.getSourcePackage(), targetMMName, query.getTargetPackage());
        GremlinScript script = ((GremlinScript) gremlinResource.getContents().get(0));
        System.out.println(script);

        MogwaiQuery gremlinInit = GremlinQueryBuilder.newBuilder()
                .fromFile(new File("materials/init.gremlin"))
//              .bind(ModelDatastore.BINDING_NAME, mapping)
                .build();

        mogwaiResource.query(gremlinInit);
        System.out.println("Init done");

        MogwaiQuery gremlinQuery = GremlinQueryBuilder.newBuilder()
                .fromString(script.toString())
//              .bind(ModelDatastore.BINDING_NAME, mapping)
                .build();

        // Put bindings here if we need them
        QueryResult result = mogwaiResource.query(gremlinQuery);
    }

Maybe you can give some hints on how I could achieve that? Thank you.

gdaniel commented 5 years ago

Hi,

I'll check this later, but here are some preliminary thoughts on your issue:

I don't want to persist the results

Gremlin-ATL does not support in-memory transformation, in fact the purpose of the tool is to persist the result of the transformation to avoid memory overhead. This being said, you can still use an in-memory Neo4j database to trick the framework and not save the result of the transformation on disk if it is an issue for you.

You should not call the ATL2Gremlin transformation from client code. If you have an ATLQuery instance you can execute it (including its transformation into Gremlin) by calling MogwaiResource.transform(ATLQuery, options). If you have some issues to specify the options let me know, I'll have a look. The tHelper error is related to the way you compute the transformation: MogwaiResource.transform performs some operations in additions to the translation of the ATL query, including the definition of runtime variables that are used to ease the computation. Since you translate it manually these variables are not defined and the generated script cannot be computed.

I also want to precise that Gremlin-ATL is currently an experimental feature of Mogwaï: while I am quite confident on the supported features of the OCL2Gremlin translation, the ATL2Gremlin lacks several ATL features, that may be an issue for you if you want to compute a complex transformation.

Could you give me more information about your use case? Are you using Mogwaï on top of NeoEMF? What is the transformation you are trying to run?

ghost commented 5 years ago

Hello Thank you for your answers. My current use case is the following. I've three business models (BM1, BM2, BM3), the transformations between each models are defined with two ATL scripts (T12, T23 to describe the transformation from M1 to M2 and M2 to M3). Only BM1 has a persistent store using NeoEMF, the other models BM2 and BM3 are not persistent. What I would like to achieve is:

  1. Extract a sub set of the information in BM1. BM1 can contain several millions of instances, but depending on the application/user interactions, only a small sub set of these instances should be returned. To do so, I need some kind of language to extract the information. Ive not found in NeoEMF how to build parametrized queries (I've not this information in the documentation/example of NeoEMF, I would be grateful if you could redirect me to some documentations). Another way to do so, would be to use CDO, but I considered this option as last resort. So I used Mogwai which provide me the necessary mechanism, but then I faced the issue mentionned above,
  2. Once I've been able to extract the instances I'm interested out of BM1, then I would simply apply successively T12 and T23 to produce the instances for BM3.

Thank you and kind regards

gdaniel commented 5 years ago

Hi,

Ok, I see what you are trying to do. Let's focus on the first transformation you use to filter the content of BM1. To do this you'll need to use the InPlaceBlueprintsTransformationHelper, the code to run such transformation should look like this

MogwaiResource mogwaiResource = // initialize your MogwaiResource from the NeoEMF resource
ATLQuery atlQuery = // initialize the ATLQuery as you did before
NeoEMFGraphDatastore datastore = new NeoEMFGraphDatastore(mogwaiResource.getBackend().getGraph());
InPlaceBlueprintsTransformationHelper tHelper = new InPlaceBlueprintsTransformationHelper(datastore);
Map<String, Object> options = new HashMap<>();
options.put(ATLQueryProcessor.TRANSFORMATION_HELPER_KEY, tHelper);
mogwaiResource.transform(atlQuery, options);

This will run the transformation and store the results in the original model's database (again, creating non-persistent output models is not supported by Gremlin-ATL). I suggest to start with a very simple transformation to check that the pipeline is working fine, like copying a single object from the source to the target.

Still, if the transformation you want to run is complex you may get some issues, because Gremlin-ATL only support a subset of ATL. Anyway, let me know if you manage to execute a simple transformation.

I didn't get your point on parameterized queries with NeoEMF, could you give me an example of such query you'd like to perform? Since NeoEMF implements the EMF API I guess we can find a way to make it work.

ghost commented 5 years ago

Hi Based on your feedbacks above, I've changed a little bit my approach:

  1. I use Gremlin-OCL to query the underlying NeoEMF storage to retrieve the instance I'm interested for BM1, this works fine
  2. Then, I apply the first ATL transformation T12 using EMFTVM, the transformation rules are applied correctly (when I check the serialized objects in XMI),
  3. When I want to proceed with the second transformation T23, I'm blocked because the objects returned can't be casted to the model definition BM2. Instead, they are of type org.eclipse.emf.ecore.impl.DynamicEObjectImpl which produce as castClassException:
    java.lang.ClassCastException: org.eclipse.emf.ecore.impl.DynamicEObjectImpl cannot be cast to studentList.StudentList
    at run.Engine.launch(Engine.java:156)
    at run.Transform.<init>(Transform.java:32)
    at run.Transform.main(Transform.java:23)

    I think this error doesn't relate to Mogwai or NeoEMF. But, as I've a small experience in EMF, maybe you can provide me some hints on where to look. Kind regards

gdaniel commented 5 years ago

Hi,

Glad you manage to make Mogwaï work for your use case! The problem you are describing does not seem to be related to Mogwaï, but to the way you register the EPackages in your application. The ClassCastException means that EMFTVM is expecting an instance of a generated object, but it receives an instance of DynamicEObject instead. Did you generate the code for your metamodels? Are you using the same EPackage for the output of T12 and the input of T23?

DyanmicEObjects are used when there is no generated code. However, EMFTVM should be able to deal with it, so you may have an consistency issue between the EPackage specified in your ATL transformation and the one used in your application's code.

Kind regards

ghost commented 5 years ago

Hi I must admit that my knowledge on the base concepts used in EMF is still very limited... However, as you wrote, EMFTVM can perfectly handle these types of objects. Regarding the other questions: I do generate the code from the metamodels, however, the transformation T12 and T23 are located in different package and different ecore files. In the meantime, I've decided to switch from NeoEMF for the persistence aspects to CDO. I've also another discussion on this aspect on ATL Forum. I'm following with interest the evolution of Mogwai and NeoEMF and I thank you for your support. Kind regards