3.3. QuickStart

This guide will help you to get started with a demonstration project in 15 minutes.

Now let’s create our first example.

3.3.1. Create your first example

Creating an application with a large amount of entities quickly results in tedious work. Entity classes contain fields, zero or more overloaded constructors and a set of getters and setters. All these entities need separate controllers, repositories and so on. Now to automate this process we will start by creating entity interfaces as well as the implementation classes.

The following example shows how to produce java pojo classes and interfaces by a pattern and a model. For a very first simple impression of how MetaFactory works, you may however want to read the T01 Hello World example first.

First we define a model:

Listing 3.4 model.xml
<?xml version="1.0" encoding="UTF-8"?>
<model xmlns="https://metafactory.io/xsd/v1/model"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://metafactory.io/xsd/v1/model
                           https://metafactory.io/xsd/v1/model.xsd">
  <package name="domain_model">
        <object name="Person">
          <attribute name="firstName" type="String" length="50" notnull="true"></attribute>
          <attribute name="lastName" type="String" length="50" notnull="true"></attribute>
          <reference name="homeAddress" type="Address" notnull="true" multiplicity="1..1"></reference>
          <reference name="workAddress" type="Address" notnull="false" multiplicity="0..1"></reference>
          <reference name="phone" type="Phone" notnull="false" multiplicity="0..n"></reference>
        </object>
        <object name="Address">
          <attribute name="streetName" type="String" length="100" notnull="true"></attribute>
          <attribute name="zipCode" type="String" length="10" notnull="false"></attribute>
          <attribute name="city" type="String" length="50" notnull="true"></attribute>
          <attribute name="country" type="String" length="50" notnull="false"></attribute>
        </object>
        <object name="Phone">
          <attribute name="number" type="String" length="30" notnull="true"></attribute>
          <attribute name="description" type="String" length="100" notnull="true"></attribute>
          <reference name="person" type="Person" notnull="true" multiplicity="1..1"></reference>
        </object>
  </package>
</model>

Various attribute values such as ‘type’, ‘length’ and ‘notnull’ are included in the above snippet. However you should know that these values by itself do nothing unless they are explicitly dealt with by the pattern. MetaFactory is designed not to use any default or hidden implementations, thereby providing transparency and control to the programmer.

Note that the <reference> element contains a attribute called ‘multiplicity’. This attribute tells the pattern how many references of the given type the object can contain. For example ‘0..n’ means that the object may contain either zero or any other number of this reference type. The pattern can for example use this attribute as a condition for creating the output code.

Now let’s look at the pattern that creates interfaces and implementations for this model data.

Listing 3.5 pattern.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?xml version="1.0" encoding="UTF-8"?>
<pattern xmlns="http://www.firstbase.nl/xsd/personaliom/pattern"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://www.firstbase.nl/xsd/personaliom/pattern
                                http://www.firstbase.nl/xsd/personaliom/pattern.xsd">
        <properties>
                <java.output.directory>java</java.output.directory>
        </properties>
        <package name="com.firstbase.ba.example6a.pojo" package="domain_model"
                 path="${pattern.property.java.output.directory}">
                <interface name="I${object.name}" foreach="object">
                        <attribute name="id" access="rw">
                                <datatype>long</datatype>
                                <apicomment>primary key</apicomment>
                        </attribute>
                        <attribute name="${attribute.name}" access="rw" foreach="attribute">
                                <datatype>${attribute.type}</datatype>
                                <apicomment>${attribute.name} property</apicomment>
                        </attribute>
                        <attribute name="${reference.name}" foreach="reference" access="rw"
                                        condition="${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1">
                                <datatype>I${reference.type}</datatype>
                                <apicomment>Reference to I${reference.type} object</apicomment>
                        </attribute>
                        <attribute name="${reference.name}Set" foreach="reference" access="rw"
                                        condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
                                <library>java.util.Set</library>
                                <datatype><![CDATA[Set<I${reference.type}>]]>
                                </datatype>
                                <apicomment>Set of I${reference.type} objects</apicomment>
                        </attribute>
                        <operation name="add${firstUpper(${reference.name})}" foreach="reference"
                                        condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
                                <apicomment>Utility method to add a object of type I${reference.type} to ${reference.name}Set</apicomment>
                                <parameter name="${firstLower(${reference.name})}">
                                        <datatype> I${reference.type}</datatype>
                                        <apicomment> object to add to ${reference.name}Set</apicomment>
                                </parameter>
                        </operation>
                        <operation name="delete${firstUpper(${reference.name})}" foreach="reference"
                                        condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
                                <parameter name="${firstLower(${reference.name})}">
                                        <datatype> I${reference.type}</datatype>
                                </parameter>
                        </operation>
                </interface>
                <class name="${object.name}Impl" foreach="object">
                        <implements>I${object.name}</implements>
                        <attribute name="id" access="rw">
                                <datatype>long</datatype>
                                <apicomment>primary key</apicomment>
                        </attribute>
                        <attribute name="${attribute.name}" foreach="attribute" access="rw">
                                <datatype>${attribute.type}</datatype>
                        </attribute>
                        <attribute name="${reference.name}" foreach="reference" access="rw"
                                        condition="${reference.multiplicity}=0..1 OR ${reference.multiplicity}=1..1">
                                <datatype> I${reference.type}</datatype>
                        </attribute>
                        <attribute name="${reference.name}Set" foreach="reference" access="rw"
                                        condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
                                <library>java.util.Set</library>
                                <library>java.util.HashSet</library>
                                <datatype><![CDATA[Set<I${reference.type}>]]></datatype>
                                <body><![CDATA[
                                        new HashSet<I${reference.type}>()
                                ]]></body>
                        </attribute>
                        <operation name="add${firstUpper(${reference.name})}" foreach="reference"
                                        condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
                                <parameter name="${firstLower(${reference.name})}">
                                        <datatype> I${reference.type}</datatype>
                                </parameter>
                                <body> ${firstLower(${reference.name})}.${getSetterName(${reference.type},${object.name})}(this);
                                        ${reference.name}Set.add(${firstLower(${reference.name})});</body>
                        </operation>
                        <operation name="delete${firstUpper(${reference.name})}" foreach="reference"
                                        condition="${reference.multiplicity}=0..n OR ${reference.multiplicity}=1..n">
                                <parameter name="${firstLower(${reference.name})}">
                                        <datatype> I${reference.type}</datatype>
                                </parameter>
                                <body> ${firstLower(${reference.name})}.${getSetterName(${reference.type},${object.name})}(null);
                                        ${reference.name}Set.remove(${firstLower(${reference.name})});</body>
                        </operation>
                </class>
        </package>
</pattern>

A few explanations regarding this pattern:

  • The <package> element tells the name of the java package that the produced class will be in. This element also has a ‘package’ attribute which refers to the group of objects that is defined in model.xml.

  • Model objects are dynamically accessed using MetaFactory’s expression language. Expressions such as ${object.name} are known at the time of execution because a pattern is always executed within the context of the part of the domain model that MetaFactory is iterating over, such as a package, object or property. Read more about expressions here.

  • <![CDATA ]> tags (see line 28) are required sometimes to escape the XML reserved symbols, such as ‘<‘

  • The attribute ‘access=”rw” (of the <attribute> element) tells MetaFactory to generate getters and setters for this attribute.

  • The <operation> element (see Operation) is used to pattern Java methods.

Now that you have updated the code of our newly created project it is time to let MetaFactory run and generate all the code (menu Transform -> Run). You can find the generated code at generated-code.

Light M Add some new data entities of your own to the model and see how that works out in the output.

Good job, you’ve just created your first code with MetaFactory.

3.3.2. Evaluation

While XML patterns help you enforce structure and consistency both in your patterns and in the resulting Java classes, using this technique only does come with limitations in flexibility.

Snippets are the power tool in Metafactory that overcome these limitations and provide you with the templating freedom of using Freemarker or Velocity. You can either evaluate a snippet into true or false as a condition to operate your xml pattern on or write complete method bodies and even classes with it.

Do you value the structured approach and out of the box functionality of xml more, or the templating freedom of snippets? Mind that a very basic XML pattern is required as a container for your snippet, but other than that you are free in how you use snippets in your patterns.

Glasses S More information about the possibilities of snippets can be found at Code Instructions Snippets.

Glasses S For more about calling snippets from your pattern check out SDA example using snippets.