Home » Modeling » QVT-OML » Swapping out EvaluationVisitor to add instrumentation (dependency injection...?)
Swapping out EvaluationVisitor to add instrumentation (dependency injection...?) [message #1251793] |
Thu, 20 February 2014 15:58 |
|
We're working on a code coverage tool for QVTo. Basically we run some transformations with specified inputs, and we want to record every mapping/helper/etc that are visited.
To do that, it seems like it would be nice to override some of the methods inside the QvtOperationalEvaluationVisitorImpl class, namely visitMappingOperation(), visitHelper(), etc.
We currently have this working the following way:
Our test cases are run by using jUnit to call classes which execute a transformation with input in a standalone way, by creating a new InternalTransformationExecutor() and running execute(). Before we call execute(), however, we perform executor.setEnvironmentFactory(ourEnvFactory), passing in a class we created that extends QvtOperationalEnvFactory, overriding its createEvaluationVisitor() method so that it provides an instance of QvtOperationalEvaluationVisitorImpl() which has the visit* operations overridden. Whew. In (pseudo)code:
public class OurEnvFactory extends QvtOperationalEnvFactory {
@Override
public EvaluationVisitor<stuff> createEvaluationVisitor(stuff)
{
return new QvtOperationalEvaluationVisitorImpl((QvtOperationalEnv)env, (QvtOperationalEvaluationEnv)evalEnv)
{
@Override
public Object visitMappingOperation(MappingOperation mappingOperation)
{
// our new code here!
return super.visitMappingOperation(mappingOperation);
}
};
}
}
// And to actually execute
...
internalExecutor.setEnvironmentFactory(ourEnvFactory); // Instrumented visitor
ExecutionDiagnostic result = internalExecutor.execute(context1, input,output);
...
Honestly, this way works for us, since we manually create the InternalTransformationExecutor anyway, and we can just say "if java argument COMPUTECOVERAGE set then set our new envFactory first" inside our code. Or ideally we actually create a new launch mode like the EclEmma tool, but internally in our code it would still have that if statement.
But it seems like there ought to be a more generic way, a la dependency injection or so? Basically, we'd ideally have it so you could take any QVTo run config you have and run it in coverage mode, which would somehow get the EvaluationVisitor swapped out with the instrumented one. Then it's useful for people besides us, and we'd like to open source this afterwards if we can.
We also thought of using the bytecode instrumenting capabilities of java.lang.instrumentor, but hopefully that's overkill.
So, for you who understand design patterns and things much better than me, is there a nicer way? Either by cleverly extending/overriding classes or hints for patches we could submit for QVTo that would make this easier?
Thanks in advance for any ideas,
Christine
[Updated on: Thu, 20 February 2014 15:58] Report message to a moderator
|
|
|
Re: Swapping out EvaluationVisitor to add instrumentation (dependency injection...?) [message #1251821 is a reply to message #1251793] |
Thu, 20 February 2014 16:30 |
Ed Willink Messages: 7671 Registered: July 2009 |
Senior Member |
|
|
Hi
You'll see that the underlying OCL EvaluationVisitor is designed tio
have decorators such as a tracer. So you don't need to derive, you can
just deciorate (provided that all the WQVTo calls observe the
getVisitor() protocol.
See org.eclipse.ocl.internal.evaluation.TracingEvaluationVisitor.
Regards
Ed Willink
On 20/02/2014 15:58, Chris Gerpheide wrote:
> We're working on a code coverage tool for QVTo. Basically we run some
> transformations with specified inputs, and we want to record every
> mapping/helper/etc that are visited.
> To do that, it seems like it would be nice to override some of the
> methods inside the QvtOperationalEvaluationVisitorImpl class, namely
> visitMappingOperation(), visitHelper(), etc.
>
> We currently have this working the following way:
> Our test cases are run by using jUnit to call classes which execute a
> transformation with input in a standalone way, by creating a new
> InternalTransformationExecutor() and running execute(). Before we call
> execute(), however, we perform
> executor.setEnvironmentFactory(ourEnvFactory), passing in a class we
> created that extends QvtOperationalEnvFactory, overriding its
> createEvaluationVisitor() method so that it provides an instance of
> QvtOperationalEvaluationVisitorImpl() which has the visit* operations
> overridden. Whew. In (pseudo)code:
>
>
> public class OurEnvFactory extends QvtOperationalEnvFactory {
>
> @Override
> public EvaluationVisitor<stuff> createEvaluationVisitor(stuff)
> {
> return new
> QvtOperationalEvaluationVisitorImpl((QvtOperationalEnv)env,
> (QvtOperationalEvaluationEnv)evalEnv)
> {
>
> @Override
> public Object visitMappingOperation(MappingOperation
> mappingOperation)
> {
> // our new code here!
>
> return super.visitMappingOperation(mappingOperation);
> return result;
> }
> };
> }
> }
>
> // And to actually execute
>
> ...
> internalExecutor.setEnvironmentFactory(ourEnvFactory); // Instrumented
> visitor
> ExecutionDiagnostic result = internalExecutor.execute(context1,
> input,output);
> ...
>
>
>
> Honestly, this way works for us, since we manually create the
> InternalTransformationExecutor anyway, and we can just say "if java
> argument COMPUTECOVERAGE set then set our new envFactory first" inside
> our code. Or ideally we actually create a new launch mode like the
> EclEmma tool, but internally in our code it would still have that if
> statement.
>
> But it seems like there ought to be a more generic way, a la
> dependency injection or so? Basically, we'd ideally have it so you
> could take any QVTo run config you have and run it in coverage mode,
> which would somehow get the EvaluationVisitor swapped out with the
> instrumented one. Then it's useful for people besides us, and we'd
> like to open source this afterwards if we can.
>
> We also thought of using the bytecode instrumenting capabilities of
> java.lang.instrumentor, but hopefully that's overkill.
> So, for you who understand design patterns and things much better than
> me, is there a nicer way? Either by cleverly extending/overriding
> classes or hints for patches we could submit for QVTo that would make
> this easier?
>
> Thanks in advance for any ideas,
> Christine
>
>
>
|
|
|
Re: Swapping out EvaluationVisitor to add instrumentation (dependency injection...?) [message #1251891 is a reply to message #1251821] |
Thu, 20 February 2014 18:13 |
|
Hi Ed,
Could you provide a little more guidance? Are you suggesting then that I could extend the EvaluationVisitorDecorator instead of the QvtOperationalEvaluationVisitorImpl, and provide the wrapped object as the return for createEvaluationVisitor() in my EnvFactory as above? Like:
public class OurEnvFactory extends QvtOperationalEnvFactory {
@Override
public EvaluationVisitor<stuff> createEvaluationVisitor(stuff)
{
return new MyVisitorDecorator(new QvtOperationalEvaluationVisitorImpl((QvtOperationalEnv)env, (QvtOperationalEvaluationEnv)evalEnv));
}
}
public class MyVisitorDecorator<stuff> extends EvaluationVisitorDecorator<stuff> {
// Would prefer not to implement InternalEvaluator since there are so many methods
private QvtOperationalEvaluationVisitorImpl delegate;
public MyVisitorDecorator(EvaluationVisitor<stuff> decorated) {
super(decorated);
if (decorated instanceof QvtOperationalEvaluationVisitorImpl) {
delegate = (QvtOperationalEvaluationVisitorImpl) decorated;
}
}
public Object visitMappingOperation(MappingOperation mappingOperation)
{
return delegate.visitMappingOperation(mappingOperation);
}
If so, I'm a little confused how to make that work, since the EvaluationVisitorDecorator doesn't implement all the interfaces that QvtOperationalEvaluationVisitorImpl needs to (like org.eclipse.m2m.internal.qvt.oml.evaluator.InternalEvaluator), and adding implementation for all the methods for InternalEvaluator is a lot (so then it puts be back to wanting to extend QvtOperationalEvaluationVisitorImpl instead of decorate it). Furthermore, if this is what you meant, is it actually nicer, since I stil have to create and set my own envFactory? Or are there other decorators/interfaces you were referring to?
Thanks again for the direction,
Christine
|
|
|
Re: Swapping out EvaluationVisitor to add instrumentation (dependency injection...?) [message #1251896 is a reply to message #1251891] |
Thu, 20 February 2014 18:17 |
Ed Willink Messages: 7671 Registered: July 2009 |
Senior Member |
|
|
Hi
Sorry, I was only outlining the underlying design. I'm afraid that I'm
not sure whether QVTo fully implements it.
Regards
Ed Willink
On 20/02/2014 18:13, Chris Gerpheide wrote:
> Hi Ed,
>
> Could you provide a little more guidance? Are you suggesting then that
> I could extend the EvaluationVisitorDecorator instead of the
> QvtOperationalEvaluationVisitorImpl, and provide the wrapped object as
> the return for createEvaluationVisitor() in my EnvFactory as above? Like:
>
>
> public class OurEnvFactory extends QvtOperationalEnvFactory {
>
> @Override
> public EvaluationVisitor<stuff> createEvaluationVisitor(stuff)
> {
> return new MyVisitorDecorator(new
> QvtOperationalEvaluationVisitorImpl((QvtOperationalEnv)env,
> (QvtOperationalEvaluationEnv)evalEnv));
> }
> }
>
>
> public class MyVisitorDecorator<stuff> extends
> EvaluationVisitorDecorator<stuff> {
> // Would prefer not to implement InternalEvaluator since there are so
> many methods
>
> private QvtOperationalEvaluationVisitorImpl delegate;
>
> public MyVisitorDecorator(EvaluationVisitor<stuff> decorated) {
> super(decorated);
> if (decorated instanceof QvtOperationalEvaluationVisitorImpl) {
> delegate = (QvtOperationalEvaluationVisitorImpl) decorated;
> }
> }
> public Object visitMappingOperation(MappingOperation
> mappingOperation)
> {
> return delegate.visitMappingOperation(mappingOperation); }
>
>
> If so, I'm a little confused how to make that work, since the
> EvaluationVisitorDecorator doesn't implement all the interfaces that
> QvtOperationalEvaluationVisitorImpl needs to (like
> org.eclipse.m2m.internal.qvt.oml.evaluator.InternalEvaluator), and
> adding implementation for all the methods for InternalEvaluator is a
> lot (so then it puts be back to wanting to extend
> QvtOperationalEvaluationVisitorImpl instead of decorate it).
> Furthermore, if this is what you meant, is it actually nicer, since I
> stil have to create and set my own envFactory? Or are there other
> decorators/interfaces you were referring to?
>
> Thanks again for the direction,
> Christine
|
|
| |
Re: Swapping out EvaluationVisitor to add instrumentation (dependency injection...?) [message #1257253 is a reply to message #1251891] |
Wed, 26 February 2014 08:10 |
Sergey Boyko Messages: 171 Registered: July 2009 |
Senior Member |
|
|
Chris Gerpheide wrote on Thu, 20 February 2014 13:13Hi Ed,
...
If so, I'm a little confused how to make that work, since the EvaluationVisitorDecorator doesn't implement all the interfaces that QvtOperationalEvaluationVisitorImpl needs to (like org.eclipse.m2m.internal.qvt.oml.evaluator.InternalEvaluator), and adding implementation for all the methods for InternalEvaluator is a lot (so then it puts be back to wanting to extend QvtOperationalEvaluationVisitorImpl instead of decorate it). Furthermore, if this is what you meant, is it actually nicer, since I stil have to create and set my own envFactory? Or are there other decorators/interfaces you were referring to?
Thanks again for the direction,
Christine
Hi Chris,
In order to follow visitor decoration pattern you'd better use of org.eclipse.m2m.internal.qvt.oml.evaluator.QvtGenericEvaluationVisitor class.
Thus class MyVisitorDecorator might look like:
class MyVisitorDecorator extends QvtGenericEvaluationVisitor {
private QvtOperationalEvaluationVisitor delegate;
public MyVisitorDecorator(QvtOperationalEvaluationVisitor qvtExtVisitor) {
super(qvtExtVisitor);
delegate = qvtExtVisitor;
}
public IContext getContext() {
return delegate.getContext();
}
public void setOperationalEvaluationEnv(QvtOperationalEvaluationEnv evalEnv) {
delegate.setOperationalEvaluationEnv(evalEnv);
}
public QvtOperationalEvaluationEnv getOperationalEvaluationEnv() {
return delegate.getOperationalEvaluationEnv();
}
}
And instantiation code in OurEnvFactory:
public EvaluationVisitor<..> createEvaluationVisitor(...) {
EvaluationVisitorDecorator<..> decorator =
new MyVisitorDecorator((QvtOperationalEvaluationVisitor) super.createEvaluationVisitor(env, evalEnv, extentMap)) {
@Override
public Object visitMappingBody(MappingBody mappingBody) {
...
return super.visitMappingBody(mappingBody);
}
};
return decorator;
}
Btw, have you looked at Eclipse AspectJ project (http://eclipse.org/aspectj/)? It provides elegant and consistent means to inject whatever logic without affecting source code.
Regards,
Sergey
|
|
|
Re: Swapping out EvaluationVisitor to add instrumentation (dependency injection...?) [message #1272224 is a reply to message #1257253] |
Mon, 17 March 2014 11:59 |
|
Hello Sergey!
Apparently I didn't have notifications set, so I didn't see this reply. I think these are great suggestions to try. I am looking now at AspectJ to see if I can use it somehow. With the first suggestion though, I'm not sure if a decorator can be created so simply:
I've been trying to use the QvtGenericEvaluationVisitor class as you suggested, but it doesn't seem to call any overridden visit* methods. I haven't yet figured out exactly where it goes awry (I still struggle with how all the internal QVT classes fit together), but maybe it's something like QVT calls a decorated method which internally in the delegate class calls other methods, thus bypassing the decorated instance. Is that possible? I do see that QvtGenericEvaluationVisitor is used by the DebugInterceptor, but that also makes use of the QVTODebugEvaluator that encapsulates it (which extends QvtOperationalEvaluationVisitorImpl), so maybe that's why that one works. So at least so far, I haven't figured out how to use a Decorator without also having a class extending QvtOperationalEvaluationVisitorImpl anyway (and then I'm not sure why to use a decorator instead of the visitor subclass directly). It would be nice if I could just use such a decorator though, since I suppose that's what it's there for.
If this is just me not understanding how to use it properly, please let me know. Or, if this is something that should be improved in the QVT code to make this easier, I'd be happy to try to submit a patch with a little guidance.
Again, thanks for the suggestions,
Chris
|
|
|
Re: Swapping out EvaluationVisitor to add instrumentation (dependency injection...?) [message #1272588 is a reply to message #1272224] |
Tue, 18 March 2014 09:11 |
|
Update: I have got a working decorator that extends QvtGenericEvaluationVisitor. It does however need (as far as I can tell) a class extending QvtOperationalEvaluationVisitorImpl to encapsulate it. At first I wasn't sure why the decorator would be useful then if I needed to have my own visitor class anyway, but at the very least the genericPostVisitAST() methods are very convenient. I've added my notes below so anyone can see what is needed to make it work.
Notes to use a visitor decorator:
- The decorator (call it OurDecorator, which extends QvtGenericEvaluationVisitor) should also implement InternalEvaluator, since the Visitor it wraps has to implement that.
- In order to implement InternalEvaluator, the decorator must be encapsulated in an instance of QvtOperationalEvaluationVisitorImpl, since one of the methods it must implement requires access to OperationCallResult, a protected nested class of QvtOperationalEvaluationVisitorImpl.
- The visitor class (call it OurVisitor, which extends QvtOperationalEvaluationVisitorImpl) must override createInterruptibleVisitor() to return itself wrapped in OurDecorator.
- In the env factory (extending QvtOperationalEnvFactory), the new MyVisitor should be returned from createEvaluationVisitor, buthave createInterruptibleVisitor() called on that visitor first.
- Then as described in the above posts, the env factory should be set on the InternalTransformationExecutor before the transformation executes.
Now, the implementation in code:
...
public class OurVisitor extends QvtOperationalEvaluationVisitorImpl implements InternalEvaluator, DeferredAssignmentListener {
public OurVisitor (QvtOperationalEnv env, QvtOperationalEvaluationEnv evalEnv) {
super(env, evalEnv);
}
@Override
protected InternalEvaluator createInterruptibleVisitor() {
return new OurDecorator(this);
}
protected class OurDecorator extends QvtGenericEvaluationVisitor implements InternalEvaluator{
private QvtOperationalEvaluationVisitorImpl delegate;
public OurDecorator(QvtOperationalEvaluationVisitorImpl qvtOperationalEvaluationVisitor) {
super(qvtOperationalEvaluationVisitor);
delegate = qvtOperationalEvaluationVisitor;
}
@Override
protected Object genericPostVisitAST(ASTNode element,
Object preVisitState, Object result) {
// Do you interesting stuff here, or in visit* methods.
return super.genericPostVisitAST(element, preVisitState, result);
}
@Override
public IContext getContext() {
return delegate.getContext();
}
@Override
public void setOperationalEvaluationEnv(QvtOperationalEvaluationEnv evalEnv) {
delegate.setOperationalEvaluationEnv(evalEnv);
}
@Override
public QvtOperationalEvaluationEnv getOperationalEvaluationEnv() {
return delegate.getOperationalEvaluationEnv();
}
@Override
public ModuleInstance callTransformationImplicitConstructor(OperationalTransformation transformation, List<ModelInstance> args) {
return delegate.callTransformationImplicitConstructor(transformation, args);
}
@Override
public OperationCallResult runMainEntry(OperationalTransformation transformation, List<Object> args) {
return delegate.runMainEntry(transformation, args);
}
@Override
public Object execute(OperationalTransformation transformation) throws QvtRuntimeException {
return delegate.execute(transformation);
}
}
}
...
public class OurEnvFactory extends QvtOperationalEnvFactory{
public QVTOCoverageEnvFactory() {
}
@Override
public
EvaluationVisitor<stuff> createEvaluationVisitor(stuff) {
return new OurVisitor((QvtOperationalEnv) env, (QvtOperationalEvaluationEnv) evalEnv).createInterruptibleVisitor();
}
}
...
public class OurExecutor extends InternalTransformationExecutor {
public OurExecutor(URI uri) {
super(uri);
}
@Override
public ExecutionDiagnostic execute(ExecutionContext executionContext,
ModelExtent... modelParameters) {
setEnvironmentFactory(new OurEnvFactory());
return super.execute(executionContext, modelParameters);
}
}
So that seems to be the current state of affairs with decorators. Seems to be a bit of a hassle (mostly the OurVisitor/OurDecorator classes have more stuff than they should need). I'm not sure the best way to improve it, perhaps start with making the QvtGenericEvaluationVisitor implement InternalEvaluator from the start (which would need OperationCallResult to become a visible class). Then perhaps it would work to just wrap our decorator around a the (new QvtOperationalEvaluationVisitorImpl().createInterruptibleVisitor()). I haven't tested those things yet though.
Chris
|
|
|
Re: Swapping out EvaluationVisitor to add instrumentation (dependency injection...?) [message #1272612 is a reply to message #1272588] |
Tue, 18 March 2014 10:31 |
Ed Willink Messages: 7671 Registered: July 2009 |
Senior Member |
|
|
Hi
We can eliminate the needs for workarounds, so that your added
functionality is a clean addition.
If you check out the QVTo sources from GIT you can modify them, raise a
Bugzilla and attach a patch of your changes for us to review. If you
want this to appear in the Luna release you need to react fast since we
are getting close to the Release Candidate stage and strictly limited
enthusiasm for changes.
Regards
Ed Willink
On 18/03/2014 09:11, Christine Gerpheide wrote:
> Update: I have got a working decorator that extends
> QvtGenericEvaluationVisitor. It does however need (as far as I can
> tell) a class extending QvtOperationalEvaluationVisitorImpl to
> encapsulate it. At first I wasn't sure why the decorator would be
> useful then if I needed to have my own visitor class anyway, but at
> the very least the genericPostVisitAST() methods are very convenient.
> I've added my notes below so anyone can see what is needed to make it
> work.
>
> Notes to use a visitor decorator:
> - The decorator (call it OurDecorator, which extends
> QvtGenericEvaluationVisitor) should also implement InternalEvaluator,
> since the Visitor it wraps has to implement that.
> - In order to implement InternalEvaluator, the decorator must be
> encapsulated in an instance of QvtOperationalEvaluationVisitorImpl,
> since one of the methods it must implement requires access to
> OperationCallResult, a protected nested class of
> QvtOperationalEvaluationVisitorImpl.
> - The visitor class (call it OurVisitor, which extends
> QvtOperationalEvaluationVisitorImpl) must override
> createInterruptibleVisitor() to return itself wrapped in OurDecorator.
> - In the env factory (extending QvtOperationalEnvFactory), the new
> MyVisitor should be returned from createEvaluationVisitor, buthave
> createInterruptibleVisitor() called on that visitor first.
> - Then as described in the above posts, the env factory should be set
> on the InternalTransformationExecutor before the transformation executes.
>
> Now, the implementation in code:
>
>
> ...
> public class OurVisitor extends QvtOperationalEvaluationVisitorImpl
> implements InternalEvaluator, DeferredAssignmentListener {
>
> public OurVisitor (QvtOperationalEnv env,
> QvtOperationalEvaluationEnv evalEnv) {
> super(env, evalEnv);
> }
>
> @Override
> protected InternalEvaluator createInterruptibleVisitor() {
> return new OurDecorator(this);
> }
>
> protected class OurDecorator extends QvtGenericEvaluationVisitor
> implements InternalEvaluator{
>
> private QvtOperationalEvaluationVisitorImpl delegate;
>
> public OurDecorator(QvtOperationalEvaluationVisitorImpl
> qvtOperationalEvaluationVisitor) {
> super(qvtOperationalEvaluationVisitor);
> delegate = qvtOperationalEvaluationVisitor;
> }
>
> @Override
> protected Object genericPostVisitAST(ASTNode element,
> Object preVisitState, Object result) {
>
> // Do you interesting stuff here, or in visit* methods.
>
> return super.genericPostVisitAST(element, preVisitState,
> result);
> }
>
> @Override
> public IContext getContext() {
> return delegate.getContext();
> }
>
> @Override
> public void
> setOperationalEvaluationEnv(QvtOperationalEvaluationEnv evalEnv) {
> delegate.setOperationalEvaluationEnv(evalEnv);
> }
>
> @Override
> public QvtOperationalEvaluationEnv
> getOperationalEvaluationEnv() {
> return delegate.getOperationalEvaluationEnv();
> }
>
> @Override
> public ModuleInstance
> callTransformationImplicitConstructor(OperationalTransformation
> transformation, List<ModelInstance> args) {
> return
> delegate.callTransformationImplicitConstructor(transformation, args);
> }
>
> @Override
> public OperationCallResult
> runMainEntry(OperationalTransformation transformation, List<Object>
> args) {
> return delegate.runMainEntry(transformation, args);
> }
>
> @Override
> public Object execute(OperationalTransformation
> transformation) throws QvtRuntimeException {
> return delegate.execute(transformation);
> }
> }
> }
>
> ...
>
> public class OurEnvFactory extends QvtOperationalEnvFactory{
>
> public QVTOCoverageEnvFactory() {
> }
>
> @Override
> public
> EvaluationVisitor<stuff> createEvaluationVisitor(stuff) {
>
> return new OurVisitor((QvtOperationalEnv) env,
> (QvtOperationalEvaluationEnv) evalEnv).createInterruptibleVisitor();
> }
> }
>
> ...
>
> public class OurExecutor extends InternalTransformationExecutor {
>
> public OurExecutor(URI uri) {
> super(uri);
> }
>
> @Override
> public ExecutionDiagnostic execute(ExecutionContext executionContext,
> ModelExtent... modelParameters) {
>
> setEnvironmentFactory(new OurEnvFactory());
> return super.execute(executionContext, modelParameters);
> }
> }
>
>
> So that seems to be the current state of affairs with decorators.
> Seems to be a bit of a hassle (mostly the OurVisitor/OurDecorator
> classes have more stuff than they should need). I'm not sure the best
> way to improve it, perhaps start with making the
> QvtGenericEvaluationVisitor implement InternalEvaluator from the start
> (which would need OperationCallResult to become a visible class).
> Then perhaps it would work to just wrap our decorator around a the
> (new
> QvtOperationalEvaluationVisitorImpl().createInterruptibleVisitor()). I
> haven't tested those things yet though.
>
> Chris
|
|
| | |
Goto Forum:
Current Time: Thu Sep 26 19:57:54 GMT 2024
Powered by FUDForum. Page generated in 0.04281 seconds
|