Register Ecore meta-model programmatically - eclipse

I use a transformation engine to create an Ecore meta-model at runtime and I wonder how we can register that meta-model with EMF so that it can recognize the meta-model?

If you have the code generated by your metamodel:
resourceSet.getPackageRegistry()
.put(org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage.eINSTANCE.getNsURI()
, org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage.eINSTANCE);
(here for the "genmodel" metamodel)
If you only have the .ecore file:
// register globally the Ecore Resource Factory to the ".ecore" extension
// weird that we need to do this, but well...
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(
"ecore", new EcoreResourceFactoryImpl());
ResourceSet rs = new ResourceSetImpl();
// enable extended metadata
final ExtendedMetaData extendedMetaData = new BasicExtendedMetaData(rs.getPackageRegistry());
rs.getLoadOptions().put(XMLResource.OPTION_EXTENDED_META_DATA,
extendedMetaData);
Resource r = rs.getResource(uriOfYourModel, true);
EObject eObject = r.getContents().get(0);
if (eObject instanceof EPackage) {
EPackage p = (EPackage)eObject;
rs.getPackageRegistry().put(p.getNsURI(), p);
}
You can find a bit more about this code here with the method named registerEcorePackages(), used to register .ecore file in the workspace (with their workspace fullpath) in our custom package registry. If you want to register your metamodel in EMF global package registry, replace resourceSet.getPackageRegistry() by EPackage.Registry.INSTANCE.

I had to modify the code from #sbegaudeau a bit for it to work:
Replace
rs.getPackageRegistry().put(p.getNsURI(), p);
with
EPackage.Registry.INSTANCE.put(p.getNsURI(), p);
Also, somehow I cannot register the .ecore type. Had to use "*": Resource.Factory.Registry.INSTANCE.
getExtensionToFactoryMap().put("*", new EcoreResourceFactoryImpl());

Related

WildFly naming subsystem: how to bind to a java.util.Properties object

I intend to define a JNDI propery of the type java.util.Properties in the WildFly application server to read it from my application.
As described in the an older WildFly documentation one can create a global binding of the type object-factory. In the example is an optional environment tag to hold multiple key/value pairs. That would map exactly my desire to get a java.util.Properties when reading a JNDI resource.
The question is weather there is already an implementiation of javax.naming.spi.ObjectFactory to create a java.util.Properties object out of an object-factory binding or do I need to implement it myself and install it as a separate module (like it is described at mastertheboss.com/...)?
Ok, I just implemented it mysef.
package com.myorg.wildfly.objectfactory;
...
public class WildFlyPropertiesObjectFactory implements ObjectFactory
{
#Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception
{
Properties p = new Properties();
if(environment != null)
{
Set<?> keySet = environment.keySet();
for(Object key : keySet)
{
p.put(key, environment.get(key));
}
}
return p;
}
}
Clean&Build this single class into a JAR file wildfly-properties-objetfactory.jar
Add a module via jboss-cli.sh
module add --name=my.jndi.propertyreader --resource=wildfly-properties-objetfactory.jar --dependencies=javax.api
Create a naming binding via jboss-cli.sh
/subsystem=nameing/binding=java\:\/my_super_cool_jndi_name:add(binding-type=object-factory, module=my.jndi.propertyreader, class=com.myorg.wildfly.objectfactory.WildFlyPropertiesObjectFactory, environment=[key1=value1, key2=value2])
And in my application I can access it in an CDI bean via
public void test() {
Properties p = (Properties) new InitialContext().lookup("java:/my_super_cool_jndi_name");
System.out.printf("Properties: %s%n", p);
}
Or as shown in the mentioned link inject it as a direct dependency #Resource(lookup = "java:/my_super_cool_jndi_name") Properties p;
Works as a charm.

reading the eobjects from the ecore file in eclipse

I have the ecore file which contains the class eobjects.Now i want to read that ecore file and get all the class eobjects from that ecore file.
Do you mean you want to reload your specific xmi file with a custom suffix?
Here is an example of a method that loads an ecore file at a specific location (path) and returns your root EObject
public static EObject loadYourModel(String path) {
/*Initialzie Models*/
YourPackage.eINSTANCE.eClass();
/*register your xmi resources*/
final Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
final Map<String, Object> m = reg.getExtensionToFactoryMap();
/*put all your different ecore file suffixes in the map; suffix = YourPackage.eNAME*/
m.put(YourPackage.eNAME, new XMIResourceFactoryImpl());
/*you can put all different package names here*/
/*Create a new Resource set to store the EObjects from the file*/
ResourceSet resSet = new ResourceSetImpl();
/*get the resource of your ecore file*/
Resource resource = resSet.getResource(URI.createURI(path), true);
/*Get the first element = root of your model hierachy*/
EObject root = resource.getContents().get(0);
return root;
}

EMF generate model class from ECore XMI at runtime

As I know, we can generate ECore model like this:
// 动态创建一个Book的子类,Magic power comes from here
// create the SchoolBook EClass
EClass schoolBookEClass = efactory.createEClass();
schoolBookEClass.setName("SchoolBook");
// create a new attribute for this EClass
EAttribute level = efactory.createEAttribute();
level.setName("level");
level.setEType(epackage.getEInt());
schoolBookEClass.getEStructuralFeatures().add(level);
// 设置父类
schoolBookEClass.getESuperTypes().add(ExtlibraryPackage.eINSTANCE.getBook());
// 创建新的课程类
EClass courseEClass = efactory.createEClass();
courseEClass.setName("Course");
// 课程名称属性
EAttribute courseName = efactory.createEAttribute();
courseName.setName("courseName");
courseName.setEType(epackage.getEString());
courseEClass.getEStructuralFeatures().add(courseName);
// 课程对教材的引用关系
EReference courseBook = efactory.createEReference();
courseBook.setName("courseBook");
courseBook.setEType(schoolBookEClass);
courseBook.setContainment(false);
courseEClass.getEStructuralFeatures().add(courseBook);
// 创建包
EPackage schoolPackage = efactory.createEPackage();
schoolPackage.setName("elv");
schoolPackage.setNsPrefix("elv");
schoolPackage.setNsURI("http:///www.elver.org/School");
schoolPackage.getEClassifiers().add(courseEClass);
schoolPackage.getEClassifiers().add(schoolBookEClass);
EPackage.Registry.INSTANCE.put(schoolPackage.getNsURI(), schoolPackage);
there is no such classes in classpath at all, but we can use them for other operation, create ui, save to database for example.
If I have some EMF model classes defined in a xml file (mymodel.ecore for example), which just contains the XMI serialization of those ECore model. Then how can I create instance of them at runtime without generated classes on classpath.
You can actually load the ECore model at runtime and instantiate objects:
ResourceSet resourceSet = new ResourceSetImpl();
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put( "ecore", new EcoreResourceFactoryImpl());
Resource resource = resourceSet.getResource(URI.createFileURI("C:\\my.ecore"), true);
EPackage model = (EPackage)resource.getContents().get(0);
EClass eClass = (EClass) model.getEClassifiers().get(0);
EObject object = model.getEFactoryInstance().create(eClass);

XText programmatically parse a DSL script into an Ecore model

I need to programmatically turn a text conform to an XText grammar into an AST conform to an Ecore meta-model generated by XText from the same grammar.
I know XText also generate the Java classes implementing such parser but I don't know either where they are and how to use it.
A complete answer to this question can be found on the Xtext page of the Eclipse wiki.
new org.eclipse.emf.mwe.utils.StandaloneSetup().setPlatformUri("../");
Injector injector = new MyDslStandaloneSetup().createInjectorAndDoEMFRegistration();
XtextResourceSet resourceSet = injector.getInstance(XtextResourceSet.class);
resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);
Resource resource = resourceSet.createResource(URI.createURI("dummy:/example.mydsl"));
InputStream in = new ByteArrayInputStream("type foo type bar".getBytes());
resource.load(in, resourceSet.getLoadOptions());
Model model = (Model) resource.getContents().get(0);
Change the file extension (mydsl) to your own language extension.
Here's the code:
#Inject
ParseHelper<Domainmodel> parser
def void parseDomainmodel() {
// When in a vanilla Java application (i.e. not within Eclipse),
// you need to run a global setup:
val injector = new MyDslStandaloneSetup().createInjectorAndDoEMFRegistration
injector.injectMembers(this) // sets the field 'parser'
// this is how you can use it:
val model = parser.parse(
"entity MyEntity {
parent: MyEntity
}")
val entity = model.elements.head as Entity
assertSame(entity, entity.features.head.type)
}
See also http://www.eclipse.org/Xtext/documentation.html#TutorialUnitTests.

Jetty Bind DataSource in JNDI Context

I would like to bind DataSource object to (eclipse) jetty's JNDI context programatically. I need for testing purpose. Here's a piece of code I have now:
server = new Server(SERVER_PORT);
webAppContext = new WebAppContext();
webAppContext.setResourceBase(".");
webAppContext.setContextPath("/" + SERVER_CONTEXT);
webAppContext.addEventListener(prepareServletContextListener());
webAppContext.addFilter(GuiceFilter.class, "/*", null);
webAppContext.addServlet(DefaultServlet.class, "/");
Resource r = new Resource(webAppContext,"jdbc/testDS",createDataSource());
server.setHandler(webAppContext);
server.start();
Of course the line with Resource isn't working.I have no idea how to bind it programatically to obtain sth similar to:
<New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
<Arg></Arg>
<Arg>jdbc/DSTest</Arg>
<Arg>
<New class="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource">
<Set name="Url">jdbc:mysql://localhost:3306/databasename</Set>
<Set name="User">user</Set>
<Set name="Password">pass</Set>
</New>
</Arg>
</New>
Any help would be greatly appreciated.
you need to add the defined resource object as attribute to the server object.
The following should work:
Resource r = new Resource("jdbc/testDS",createDataSource());
server.setAttribute("myDs", r);
I encountered the same issue and here is what I did to fix it:
server = new Server(PORT);
WebAppContext context = new WebAppContext();
context.setContextPath(CONTEXT);
context.setConfigurationClasses(new String[] { "org.eclipse.jetty.plus.webapp.PlusConfiguration",
"org.eclipse.jetty.webapp.FragmentConfiguration" });
// A filter needed by Guice, but this is independent
context.addFilter(GuiceFilter.class, "/*", 0);
PGSimpleDataSource simpleDataSource = new PGSimpleDataSource();
simpleDataSource.setDatabaseName("db-name");
simpleDataSource.setUser("user");
simpleDataSource.setPassword("pwd");
String jndiName = "jdbc/myDS";
Resource resource = new Resource("java:comp/env/" + jndiName, simpleDataSource);
server.setHandler(context);
// an event listener (because I use Guice, but this is independent)
GuiceServletConfig guiceServletConfig = new GuiceServletConfig();
context.addEventListener(guiceServletConfig);
server.start();
This requires to have the following additional libraries:
jetty-jndi
jetty-plus
I tested this code on Embedded Jetty 7 (7.6.10.v20130312)
The topic is old but I have encountered the same issue. Unfortunately existing answers provide no solution.
When you create a Resource specifying an instance of the DataSource, Jetty will not provide this instance when JNDI resource is requested. Instead constructor of Resource makes an attempt to transform the instance to some kind of "recipe" (javax.naming.Reference to be precise) that tells how to build the new instance of configuration with exact same internals.
It works well in case of basic classes but fails to recreate complex data structures (it failed with List<String> in my case). So you end up not getting:
the exact same instance (as you expect of a singleton)
the exact equivalent of the instance (as you expect of properly working JNDI)
I have implemented a wrapper that allows providing the instance specified when creating the Resource.
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
public class ExistingInstanceReference extends Reference implements ObjectFactory {
private Object instance;
private static final long serialVersionUID = -2718709718400909747L;
public ExistingInstanceReference() {
super(null);
}
public ExistingInstanceReference(Object instance) {
super(instance.getClass().getName(), ExistingInstanceReference.class.getName(), null);
this.instance = instance;
}
#Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
if (obj == null) {
return null;
}
ExistingInstanceReference reference = (ExistingInstanceReference) obj;
return reference.instance;
}
}
Note that this implementation fails to comply with Serializable interface as instance variable may contain non-serializable object.
Usage:
Resource r = new Resource(webAppContext, "jdbc/testDS", new ExistingInstanceReference(createDataSource()));
Please, consider this solution as a workaround because the real issue is still to be fixed somewhere in Jetty's sources. Unfortunately I have no time to trace this misbehavior to the root.
In addition, I had to set webAppContext.setParentLoaderPriority(true) because the webapp's classloader didn't see ExistingInstanceReference class in my setup.