Home » Modeling » TMF (Xtext) » inject extension not injected in JUnit test(inject extension not injected in JUnit test)
inject extension not injected in JUnit test [message #964519] |
Tue, 30 October 2012 15:38 |
Michael Veit Messages: 12 Registered: January 2012 |
Junior Member |
|
|
Hello,
I want to run a model to model transformation with Xtend 2.3.1, without having a Xtext grammar.
My problem is:
When using the @Inject extension, it does not get injected.
When I run an JUnit test, the injected type is null and therefore creating a NullPointerException during runtime.
I started with a migration from Xtend 1, where the grammar was like:
extension my::package::classname_dt;
create targetmodel::Application mapApplication(Application app, targetmodel::Model model):
[...]
With Xtend2, it looks like:
class MyClass {
@Inject extension ClassnameDt
def targetmodel.Application create TargetmodelFactory::eINSTANCE.createApplication mapApplication(
sourcemodel.Application app,
targetmodel.Model model
) {
[...]
}
}
As I use a create method, I cannot inject the ClassnameDt as static extension, which would have solved my problem.
In the test class, I use
@RunWith(XtextRunner.class)
@InjectWith(InjectorProviderCustom.class)
public class TargetmodelFactoryServiceTest extends AbstractTargetmodelFactoryTest {
@Test
public void testAllParameterTypes() throws Exception {
[...]
}
}
which calls the transform() method of the TargetmodelFactory:
Injector injector = new mypackage.TargetmodelFactoryStandaloneSetup().createInjectorAndDoEMFRegistration();
//Injector injector = new Mwe2StandaloneSetup().createInjectorAndDoEMFRegistration();
//Injector injector = new XtendStandaloneSetup().createInjectorAndDoEMFRegistration();
Mwe2Runner mweRunner = injector.getInstance(Mwe2Runner.class);
//mweRunner.run(URI.createFileURI(mweFile.getAbsolutePath()), empty);
mweRunner.run(URI.createFileURI("myworkflow.mwe2"), prepareWorkflowProperties(), slots);
I tried several methods how to create the StandaloneSetup.
The TargetmodelFactoryStandaloneSetup looks like:
public class TargetmodelFactoryStandaloneSetup extends Mwe2StandaloneSetupGenerated {
public static void doSetup() {
new TargetmodelFactoryStandaloneSetup().createInjectorAndDoEMFRegistration();
}
public Injector createInjectorAndDoEMFRegistration() {
org.eclipse.xtext.xbase.XbaseStandaloneSetup.doSetup();
// register default ePackages
if (!Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("ecore"))
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(
"ecore", new org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl());
if (!Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("xmi"))
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(
"xmi", new org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl());
if (!EPackage.Registry.INSTANCE.containsKey(org.eclipse.xtext.XtextPackage.eNS_URI))
EPackage.Registry.INSTANCE.put(org.eclipse.xtext.XtextPackage.eNS_URI, org.eclipse.xtext.XtextPackage.eINSTANCE);
Injector injector = createInjector();
register(injector);
return injector;
}
public Injector createInjector() {
return Guice.createInjector(Modules2.mixin(
new mypackage.TargetmodelFactoryRuntimeModule(),
new org.eclipse.emf.mwe2.language.Mwe2RuntimeModule()
));
}
public void register(Injector injector) {
if (!EPackage.Registry.INSTANCE.containsKey("TargetmodelFactory")) {
EPackage.Registry.INSTANCE.put("TargetmodelFactory", mypackage.TargetmodelFactory.eINSTANCE);
}
// Copied from Mwe2StandaloneSetupGenerated:
org.eclipse.xtext.resource.IResourceFactory resourceFactory = injector.getInstance(org.eclipse.xtext.resource.IResourceFactory.class);
org.eclipse.xtext.resource.IResourceServiceProvider serviceProvider = injector.getInstance(org.eclipse.xtext.resource.IResourceServiceProvider.class);
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("mwe2", resourceFactory);
org.eclipse.xtext.resource.IResourceServiceProvider.Registry.INSTANCE.getExtensionToFactoryMap().put("mwe2", serviceProvider);
}
}
The RuntimeModule looks like:
public class TargetmodelFactoryRuntimeModule extends AbstractGenericResourceRuntimeModule {
// contributed by org.eclipse.xtext.generator.types.TypesGeneratorFragment
public org.eclipse.xtext.common.types.TypesFactory bindTypesFactoryToInstance() {
return org.eclipse.xtext.common.types.TypesFactory.eINSTANCE;
}
@Override
protected String getLanguageName() {
return "targetmodel";
}
@Override
protected String getFileExtensions() {
return "mod";
}
}
The InjectorProviderCustom is a copy from the domainmodel example:
public class InjectorProviderCustom extends TargetmodelFactoryInjectorProvider {
public Injector getInjector() {
if (injector == null) {
stateBeforeInjectorCreation = GlobalRegistries.makeCopyOfGlobalState();
this.injector = new TargetmodelFactoryStandaloneSetup() {
//@Override
//public Injector createInjector() {
// return Guice.createInjector(new SMMFactoryRuntimeModule() {
//@Override
//public ClassLoader bindClassLoaderToInstance() {
// return InjectorProviderCustom.class.getClassLoader();
//}
// });
//}
}.createInjectorAndDoEMFRegistration();
stateAfterInjectorCreation = GlobalRegistries.makeCopyOfGlobalState();
}
return injector;
}
}
Does anyone have any idea how to get these Xtend classes injected?
[Updated on: Wed, 31 October 2012 07:03] Report message to a moderator
|
|
|
Re: inject extension not injected in JUnit test [message #964852 is a reply to message #964519] |
Tue, 30 October 2012 21:03 |
Sebastian Zarnekow Messages: 3118 Registered: July 2009 |
Senior Member |
|
|
Hi Michael,
where does the instanceof of MyClass come from that is used in the test,
e.g. how does testAllParameterTypes look like? Please note that MWE is
usually not required in unit tests and it does not use dependency
injection out of the box.
Regards,
Sebastian
--
Looking for professional support for Xtext, Xtend or Eclipse Modeling?
Go visit: http://xtext.itemis.com
Am 30.10.12 16:38, schrieb Michael Veit:
> Hello,
>
> I want to run a model to model transformation with Xtend 2.3.1, without
> having a Xtext grammar.
>
> My problem is:
> When using the @Inject extension, it does not get injected.
> When I run an JUnit test, the injected type is null and therefore
> creating a NullPointerException during runtime.
>
> I started with a migration from Xtend 1, where the grammar was like:
>
> extension my::package::classname_dt;
>
> create targetmodel::Application mapApplication(Application app,
> targetmodel::Model model):
> [...]
>
>
> With Xtend2, it looks like:
>
> class MyClass {
> @Inject extension ClassnameDt
>
> def targetmodel.Application create
> TargetmodelFactory::eINSTANCE.createApplication mapApplication(
> sourcemodel.Application app,
> targetmodel.Model model
> ) {
> [...]
> }
> }
>
>
> As I use a create method, I cannot inject the ClassnameDt as static
> extension, which would have solved my problem.
>
> In the test class, I use
> @RunWith(XtextRunner.class)
> @InjectWith(InjectorProviderCustom.class)
> public class TargetmodelFactoryServiceTest extends
> AbstractTargetmodelFactoryTest {
>
> @Test
> public void testAllParameterTypes() throws Exception {
> [...]
> }
> }
>
> which calls the transform() method of the TargetmodelFactory:
> Injector injector = new
> mypackage.TargetmodelFactoryStandaloneSetup().createInjectorAndDoEMFRegistration();
>
> //Injector injector = new
> Mwe2StandaloneSetup().createInjectorAndDoEMFRegistration();
> //Injector injector = new
> XtendStandaloneSetup().createInjectorAndDoEMFRegistration();
> Mwe2Runner mweRunner = injector.getInstance(Mwe2Runner.class);
> //mweRunner.run(URI.createFileURI(mweFile.getAbsolutePath()), empty);
> mweRunner.run(URI.createFileURI("myworkflow.mwe2"),
> prepareWorkflowProperties(), slots);
>
>
> I tried several methods how to create the StandaloneSetup.
>
> The TargetmodelFactoryStandaloneSetup looks like:
> public class TargetmodelFactoryStandaloneSetup extends
> Mwe2StandaloneSetupGenerated {
>
> public static void doSetup() {
> new
> TargetmodelFactoryStandaloneSetup().createInjectorAndDoEMFRegistration();
> }
>
> public Injector createInjectorAndDoEMFRegistration() {
> org.eclipse.xtext.xbase.XbaseStandaloneSetup.doSetup();
>
> // register default ePackages
> if
> (!Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("ecore"))
>
>
> Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(
> "ecore", new
> org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl());
> if
> (!Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("xmi"))
>
>
> Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(
> "xmi", new
> org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl());
> if
> (!EPackage.Registry.INSTANCE.containsKey(org.eclipse.xtext.XtextPackage.eNS_URI))
>
>
> EPackage.Registry.INSTANCE.put(org.eclipse.xtext.XtextPackage.eNS_URI,
> org.eclipse.xtext.XtextPackage.eINSTANCE);
>
> Injector injector = createInjector();
> register(injector);
> return injector;
> }
>
> public Injector createInjector() {
> return Guice.createInjector(Modules2.mixin(
> new mypackage.TargetmodelFactoryRuntimeModule(),
> new org.eclipse.emf.mwe2.language.Mwe2RuntimeModule()
> ));
> }
>
> public void register(Injector injector) {
>
> if
> (!EPackage.Registry.INSTANCE.containsKey("TargetmodelFactory")) {
> EPackage.Registry.INSTANCE.put("TargetmodelFactory",
> mypackage.TargetmodelFactory.eINSTANCE);
> }
>
>
> // Copied from Mwe2StandaloneSetupGenerated:
> org.eclipse.xtext.resource.IResourceFactory resourceFactory =
> injector.getInstance(org.eclipse.xtext.resource.IResourceFactory.class);
> org.eclipse.xtext.resource.IResourceServiceProvider
> serviceProvider =
> injector.getInstance(org.eclipse.xtext.resource.IResourceServiceProvider.class);
>
>
> Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("mwe2", resourceFactory);
>
>
> org.eclipse.xtext.resource.IResourceServiceProvider.Registry.INSTANCE.getExtensionToFactoryMap().put("mwe2",
> serviceProvider);
> }
> }
>
>
> The RuntimeModule looks like:
> public class TargetmodelFactoryRuntimeModule extends
> AbstractGenericResourceRuntimeModule {
>
>
> // contributed by
> org.eclipse.xtext.generator.types.TypesGeneratorFragment
> public org.eclipse.xtext.common.types.TypesFactory
> bindTypesFactoryToInstance() {
> return org.eclipse.xtext.common.types.TypesFactory.eINSTANCE;
> }
>
> @Override
> protected String getLanguageName() {
> return "targetmodel";
> }
>
> @Override
> protected String getFileExtensions() {
> return "mod";
> }
>
> }
>
> The InjectorProviderCustom is a copy from the domainmodel example:
> public class InjectorProviderCustom extends
> TargetmodelFactoryInjectorProvider {
>
> public Injector getInjector() {
> if (injector == null) {
> stateBeforeInjectorCreation =
> GlobalRegistries.makeCopyOfGlobalState();
> this.injector = new TargetmodelFactoryStandaloneSetup() {
> //@Override
> //public Injector createInjector() {
> // return Guice.createInjector(new
> SMMFactoryRuntimeModule() {
> //@Override
> //public ClassLoader bindClassLoaderToInstance() {
> // return
> InjectorProviderCustom.class.getClassLoader();
> //}
> // });
> //}
> }.createInjectorAndDoEMFRegistration();
> stateAfterInjectorCreation =
> GlobalRegistries.makeCopyOfGlobalState();
> }
> return injector;
> }
> }
>
>
>
> Does anyone have any idea how to get these Xtend classes injected?
|
|
|
Re: inject extension not injected in JUnit test [message #965392 is a reply to message #964852] |
Wed, 31 October 2012 07:41 |
Michael Veit Messages: 12 Registered: January 2012 |
Junior Member |
|
|
Hi Sebastian,
in the MWE2 workflow, I call
component = mypackage.MyRootClass {
metaModel = mod
metaModel = java
outputSlot = "outputmodel"
}
which implements AbstractExpressionsUsingWorkflowComponent:
import org.eclipse.xtend.expression.AbstractExpressionsUsingWorkflowComponent
class MyRootClassextends AbstractExpressionsUsingWorkflowComponent {
@Inject extension MyClass
[...]
/** Entry point of transformation. */
def mypackage.TargetModel create mypackage::TargetmodelFactory::eINSTANCE.createModel main(
Collection<BaseException> sysExes
) {
if (sysExes.size > 0) {
sysExes.forEach(e | e.mapSysEx(it))
}
[...]
}
/** Call main(). Replaces invoke of XtendComponent in Xtend 1.x. */
override invoke(IWorkflowContext ctx) {
val mySystemExceptions = ctx.get("mySystemExceptions") as List<BaseException>
val result = main(mySystemExceptions)
ctx.put(outputSlot, result);
}
[...]
}
which gives a NullPointerException when executing e.mapSysEx(it). With debugging I verified that the Collection is not null and does not contain null entries. The _myclass instance is null and causes the exception. All parameters passed by the MWE2 workflow contain the expected values.
The testAllParameterTypes() is just plain Java, calling the transform() method of the TargetmodelFactory, which runs the standalone MWE2 workflow:
import org.junit.Before;
import org.junit.runner.RunWith;
import mypackage.mock.model.elements.ElementBuilder;
import org.eclipse.xtext.junit4.InjectWith;
import org.eclipse.xtext.junit4.XtextRunner;
[...]
@RunWith(XtextRunner.class)
@InjectWith(InjectorProviderCustom.class)
public class TargetmodelFactoryServiceTest extends AbstractTargetmodelFactoryTest {
ElementBuilder b;
@Before
public void setup() {
b = new ElementBuilder();
}
@Test
public void testAllParameterTypes() throws Exception {
[...]
b.addIn(si, "inparam", 0, 1, dt);
b.addOut(si, "outparam", 0, 1, dt);
si.addBusinessException("BE123456", "SomeBusinessException",
"description...", null);
File modelFile = null;
modelFile = File.createTempFile("testModel", "smm");
new TargetmodelFactory(modelFile).transform(b.getSysExes());
}
With Xtend1, it was possible to run the JUnit test, without this huge amount of helper classes.
|
|
| |
Re: inject extension not injected in JUnit test [message #976252 is a reply to message #965408] |
Thu, 08 November 2012 12:37 |
Michael Veit Messages: 12 Registered: January 2012 |
Junior Member |
|
|
Finally I got it running. I just want to share the knowledge in case that anyone else gets such a problem, too.
Basically I had to pass the injector to the workflowcomponent and be sure to create the instance of the Xtend class using that injector. Afterwards everything is initialized by the injection mechanism.
Here is the solution:
I implemented a StandaloneSetup, which looks like:
package mypackage;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.mwe2.language.Mwe2StandaloneSetup;
import org.eclipse.emf.mwe2.language.Mwe2StandaloneSetupGenerated;
import org.eclipse.xtext.ISetup;
import org.eclipse.xtext.util.Modules2;
import com.google.inject.Guice;
import com.google.inject.Injector;
import mypackage.MyFactory;
@SuppressWarnings("all")
public class SMMFactoryStandaloneSetup extends Mwe2StandaloneSetupGenerated {
@Override
public Injector createInjector() {
return Guice.createInjector(Modules2.mixin(
new mypackage.MyFactoryRuntimeModule(),
new org.eclipse.emf.mwe2.language.Mwe2RuntimeModule()
));
}
public void register(Injector injector) {
if (!EPackage.Registry.INSTANCE.containsKey("MyFactory")) {
EPackage.Registry.INSTANCE.put("MyFactory", mypackage.MyFactory.eINSTANCE);
}
// Copied from Mwe2StandaloneSetupGenerated:
super.register(injector);
}
}
The RuntimeModule looks like:
package mypackage;
import org.eclipse.xtext.resource.generic.AbstractGenericResourceRuntimeModule;
import org.eclipse.xtext.service.SingletonBinding;
/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
*/
@SuppressWarnings("all")
public class MyFactoryRuntimeModule extends AbstractGenericResourceRuntimeModule {
@SingletonBinding
public Class<MyClass1> bindMyClass1() {
return MyClass1.class;
}
@SingletonBinding
public Class<MyClass2> bindMyClass2() {
return MyClass2.class;
}
@Override
protected String getLanguageName() {
return "mypackage.targetmodel";
}
@Override
protected String getFileExtensions() {
return "extension";
}
}
When I call the workflow, I add the injector to the context. This is required for in order to have the injector initialized when using my Xtend classes.
IWorkflowContext slots = new WorkflowContextImpl();
Injector injector = new MyFactoryStandaloneSetup().createInjectorAndDoEMFRegistration();
slots.put("injector", injector);
Mwe2Runner mweRunner = injector.getInstance(Mwe2Runner.class);
mweRunner.run(URI.createFileURI("myworkflow.mwe2"), new HashMap<String, String>(), slots);
Finally, my Xtend class looks like:
package mypackage
import com.google.inject.Inject
import com.google.inject.Singleton
import org.eclipse.emf.mwe2.runtime.workflow.IWorkflowContext
import static extension com.mypackage.XtendClassWithStaticMethods.*
@Singleton
class MyClass1 extends AbstractMyWorkflowComponent {
@Inject extension MyClass2
// Not sure if this is required for @Singleton
@Inject new() {}
[...]
/** Call main(). Replaces invoke of XtendComponent in Xtend 1.x. */
override invoke(IWorkflowContext ctx) {
// Necessary for injection
val MyClass1 obj = super.injectMembers(ctx, this) as MyClass1
val result = obj.main(myParameters)
ctx.put(outputSlot, result)
}
}
The injection will then be initialized here, using obj.getInstance(). This initializes all extensions and solved my problem:
package mypackage;
import com.google.inject.Injector;
import org.eclipse.emf.mwe2.runtime.workflow.IWorkflowContext;
import org.eclipse.xtend.expression.AbstractExpressionsUsingWorkflowComponent;
public abstract class AbstractMyWorkflowComponent extends AbstractExpressionsUsingWorkflowComponent {
protected Injector injector;
protected String outputSlot = null;
public void setOutputSlot(final String outputSlot) {
this.outputSlot = outputSlot;
}
/** Inject members. */
protected Object injectMembers(final IWorkflowContext ctx, Object obj) {
injector = (Injector) ctx.get("injector");
//injector.injectMembers(obj);
return injector.getInstance(obj.getClass());
}
}
So your hint "Where does the instance come from" was good in order to get a deeper dive into the injection mechanism and to solve it. Thanks Sebastian.
|
|
|
Goto Forum:
Current Time: Fri Sep 20 11:22:05 GMT 2024
Powered by FUDForum. Page generated in 0.05002 seconds
|