├── .project
├── Introduction.md
├── Modeling
├── README.md
└── files
│ ├── openMDX-Modeling-Guide-Part1.pdf
│ └── openMDX-Modeling-Guide-Part2.pdf
├── README.md
├── Sdk
├── BuildFromSource.md
├── Examples.md
├── README.md
├── Workshop.md
└── files
│ ├── Examples
│ ├── Examples.p007.png
│ ├── Examples.p108.png
│ ├── Examples.p110.png
│ ├── Examples.p111.png
│ ├── Examples.p115.png
│ ├── Examples.p120.png
│ ├── Examples.p125.png
│ ├── Examples.p130.png
│ ├── Examples.p140.png
│ ├── Examples.p150.png
│ ├── Examples.p160.png
│ ├── Examples.p170.png
│ ├── Examples.p180.png
│ ├── Examples.p190.png
│ ├── Examples.p200.png
│ ├── Examples.p210.png
│ ├── Examples.p220.png
│ └── Examples.p230.png
│ └── Workshop
│ ├── Workshop.pic010.png
│ ├── Workshop.pic020.png
│ └── Workshop.pic030.png
└── files
├── Overview.p010.png
└── Overview.p020.png
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | openMDX ~ Documentation
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction to openMDX #
2 |
3 | _openMDX_ is a model-driven application framework which helps you in
4 | writing service-oriented, _MDA_-based applications. Standards such as
5 | _JDO_, _MDA_, _REST_ and _JCA_ build the corner stones of
6 | _openMDX_. _openMDX_ implements, combines and integrates these
7 | standards resulting in a fully working, lightweight application framework.
8 |
9 | # Overview #
10 | At the heart of _openMDX_ is the JDO-compliant _openMDX_
11 | persistence manager. But other than most _JDO_ persistence
12 | managers, the _openMDX_ persistence manager does not store objects on
13 | the database. Instead, it acts as a proxy which is capable of
14 | delegating to one or more (local or remote) persistence managers.
15 | The most important features of the _openMDX_ persistence manager are:
16 |
17 | * __Pluggable__: Plug-Ins implementing business-logic can be
18 | registered with the persistence manager. If an application invokes
19 | a method on a persistence capable object, the _openMDX_ persistence
20 | manager intercepts this invocation and dispatches the call to the
21 | appropriate plug-in.
22 | * __Distributable__: The _openMDX_ persistence manager can either
23 | delegate to local or remote persistence managers. The protocol
24 | used for the local communication is _REST/JCA_ and for the remote
25 | communication _REST/HTTP_. _openMDX_ provides the necessary
26 | _REST_ infrastructure. The persistence manager is configured
27 | with a _jdoconfig.xml_ configuration file. Whether local or
28 | remote transports are used is completely transparent to the
29 | application.
30 |
31 | The _openMDX_ persistence manager allows to write simple, distributable
32 | and portable applications. For example a hello world client which
33 | invokes a _sayHello()_ service looks as follows:
34 |
35 | ~~~~~~
36 | :::java
37 | import org.openmdx.example.helloworld1.*;
38 |
39 | javax.jdo.PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("EntityManagerFactory");
40 | javax.jdo.PersistenceManager pm = pmf.getPersistenceManager("guest", "guest");
41 | HelloWorld helloWorld = (HelloWorld)pm.getObjectById(HELLOWORLD_XRI);
42 | HelloWorldPackage helloWorldPkg =
43 | (HelloWorldPackage)helloWorld.refOutermostPackage().refPackage(Helloworld1Package.class.getName());
44 | pm.currentTransaction().begin();
45 | org.openmdx.example.helloworld1.SayHelloResult hResult = helloWorld.sayHello(
46 | helloWorldPkg.createHelloWorldSayHelloParams("en")
47 | );
48 | pm.currentTransaction().commit();
49 | System.out.println("Client: sayHello[en]=" + hResult.getMessage());
50 | ~~~~~~
51 |
52 | __NOTE__:
53 |
54 | * The _openMDX_ persistence manager factory is looked up with the
55 | configuration-id _EntityManagerFactory_ which is configured in
56 | the _jdoconfig.xml_.
57 | * The application client does not know whether the _sayHello()_ service
58 | is deployed locally or remote.
59 | * The transaction (or unit of work) handling is either optimistic or
60 | non-optimistic according to the configuration of the persistence
61 | manager.
62 | * Java interfaces such as _HelloWorldPackage_, _HelloWorld_,
63 | _SayHelloParams_, _SayHelloResult_ are generated from the
64 | _HelloWorld_ _UML_ model.
65 | * Each _openMDX_-based application is automatically a service, or more
66 | precisely a _RESTful_ service. In case the _HelloWorld_ service is
67 | deployed as remote service, the operation _sayHello()_ can be invoked
68 | with a _POST_ request and the resource with the XRI _HELLOWORLD_XRI_
69 | can be retrieved with a _GET_ request by any _REST_ client.
70 |
71 | In addition, _openMDX_ provides tools which are used at development time
72 | such as the model mappers. They are responsible to map a _MOF_-compliant
73 | class diagram to corresponding Java interfaces, _XML_ schemas, _JPA_
74 | classes and _ORM_ mapping files.
75 |
76 | # Key concepts #
77 |
78 | ## JDO ##
79 | The _Java Data Objects (JDO)_ API is a standard interface-based Java
80 | model abstraction of persistence, developed under the auspices of the
81 | Java Community Process. The original JDO 1.0 is Java Specification
82 | Request 12 (JSR 12), and the current JDO 2.0 is Java Specification
83 | Request 243 (JSR 243). The benefits of using _JDO_ for application
84 | programming are:
85 |
86 | * __Ease of use__: Application programmers can focus on their domain
87 | object model and leave the details of persistence (field-by-field
88 | storage of objects) to the JDO implementation.
89 | * __Portability__: Applications written with the _JDO_ API can be run
90 | on multiple implementations without recompiling or changing source
91 | code. Metadata, which describes persistence behavior external to
92 | the Java source code including most commonly used features of O/R
93 | mapping, is highly portable.
94 | * __Database independence__: Applications written with the _JDO_ API
95 | are independent of the underlying database. _JDO_ implementations
96 | support many different kinds of transactional data stores, including
97 | relational and object databases, _XML_, flat files, and others.
98 | * __High performance__: Application programmers delegate the details
99 | of persistence to the _JDO_ implementation, which can optimize data
100 | access patterns for optimal performance.
101 |
102 | However, the _JDO_ and _JPA_ (Java Persistence Architecture)
103 | leaves an important question unanswered: where comes the business
104 | logic? One can put all or part of the business logic into the persistence
105 | capable (or entity) classes (or into a library which is invoked by the
106 | persistence capable class). Or one can put an application-layer on top
107 | of the persistence layer. However, all these approaches have drawbacks:
108 |
109 | * There is no clean separation between business and persistence
110 | logic
111 | * The business logic is located in the persistence tier instead of in
112 | the application tier
113 | * Introducing an application layer also introduces a different style
114 | of architecture and programming. Code which delegates to the
115 | application layer looks different than code which delegates to
116 | the persistence layer. This way code is layer-specific and hence
117 | not as portable and reusable as should be.
118 |
119 | The _openMDX_ persistence manager addresses these issues as follows:
120 |
121 | * The _openMDX_ persistence manager is a JDO-compliant implementation
122 | which acts as a proxy to one or more (local or remote) persistence
123 | managers. By default all invocations are delegated to one of the
124 | configured proxies (routing is configurable).
125 | * The business logic is isolated into separate classes, called aspect
126 | oriented plug-ins (AOP). Persistence capable classes do not contain
127 | any business logic. Their only responsibility is to make an object
128 | persistent. As a consequence, the persistence capable classes can
129 | be generated, so no programming is required at this level. AOPs
130 | are registered with the _openMDX_ persistence manager. If an
131 | application invokes a method of a persistence capable, the _openMDX_
132 | persistence manager intercepts the invocation and dispatches the
133 | call to the registered AOPs. In case no AOP intercepts the
134 | invocation, the call is forwarded to one of the configured delegation
135 | persistence mangers.
136 | * The _openMDX_ persistence manager is stackable. This allows to
137 | split the business logic into layers, i.e. different aspects
138 | (business logic, generic functions such as auditing or access
139 | control) of the application logic can be completely separated.
140 |
141 | ## REST and JCA ##
142 | The openMDX persistence manager acts as a proxy persistence manager
143 | and allows to delegate to one or more (local or remote) persistence
144 | managers. The protocol used for communication between persistence
145 | managers is _REST_:
146 |
147 | * Object retrievals are mapped to a _GET _
148 | * Queries are mapped to a _GET _
149 | * Object modifications are mapped to _PUT _
150 | * Object creations are mapped to _POST _
151 | * Method invocations are mapped to _POST _
152 | * Object removals are mapped to _DELETE _
153 |
154 | _REST/HTTP_ is used for remote (inter-process) and _REST/JCA_ for
155 | local communication between persistence mangers. What is _REST/JCA_?
156 | _REST_ was initially described in the context of _HTTP_, but is
157 | not limited to that protocol. _RESTful_ architectures can be based
158 | on other application layer protocols if they already provide a rich and
159 | uniform vocabulary for applications based on the transfer of meaningful
160 | representational state.
161 |
162 | The _Java EE Connector Architecture (JCA)_ ([http://jcp.org/aboutJava/communityprocess/final/jsr322/index.html JCA])
163 | provides a suitable application layer protocol. The _REST_
164 | methods _GET_, _PUT_, _POST_ and _DELETE_ are mapped
165 | to corresponding _REST_ interaction specifications. The remote
166 | persistence manager is treated as a _JCA_ resource. Using
167 | _REST/JCA_ an object retrieval looks as follows:
168 |
169 | ~~~~~~
170 | :::java
171 | javax.resource.cci.Connection conn = connectionFactory.getConnection(
172 | new RestConnectionSpec(user, password)
173 | );
174 | javax.resource.cci.RecordFactory rf = connectionFactory.getRecordFactory();
175 | javax.resource.cci.Interaction interaction = conn.createInteraction();
176 | InteractionSpec ispec = InteractionSpecs.getRestInteractionSpecs(true).GET;
177 | javax.resource.cci.IndexedRecord input = factory.createIndexedRecord(Multiplicities.LIST);
178 | input.add(object_xri);
179 | javax.resource.cci.Record output = interaction.execute(ispec, input);
180 | ~~~~~~
181 |
182 | Output contains the retrieved object in a _JCA_ record.
183 |
184 | Using _REST_ as unified communication protocol has the following
185 | advantages:
186 |
187 | * __Independent deployment of components__: an application using the
188 | _openMDX_ persistence manager can either be deployed in-process or
189 | fully distributed.
190 | * __Integration__: A persistence manager configured for _REST/HTTP_
191 | can be accessed by any 3rd party _REST_ client. This allows easy
192 | integration of _openMDX_-based applications with non-_openMDX_
193 | applications and even with non-Java applications.
194 | * __Lightweight__: The implementation of _REST/HTTP_ and
195 | _REST/JCA_ is lightweight. All that is required on client-side
196 | is a _HttpUrlConnection_ and on server-side a WebApp container
197 | such as _Tomcat_.
198 | * __Scalability__: An application using the _openMDX_ persistence
199 | manager is scalable out-of-the box. A persistence manager deployed
200 | under _Tomcat_ can handle multiple sessions and concurrent requests
201 | (such as all _HTTP_ servlets can).
202 |
203 | ## MDA ##
204 | Based on _OMGs_ established standards such as
205 |
206 | * Unified Modeling Language (UML)
207 | * MetaObject Facility (MOF )
208 | * XML Metadata Interchange (XMI)
209 | * Common Warehouse Metamodel (CWM)
210 |
211 | the _OMG's Model Driven Architecture_ ([http://www.omg.org/mda MDA])
212 | standards separate business and application logic from the underlying
213 | platform technology. Platform-independent models (_PIMs_) of an
214 | application or integrated systems's business functionality and
215 | behaviour, built using _UML_ and the other associated _OMG_ modeling
216 | standards, can be realized through the _MDA_ on virtually any platform,
217 | open or proprietary, including WebServices, _.NET_, _CORBA_,
218 | _J2EE_, and others. These platform-independent models document
219 | the business functionality and behaviour of an application separate
220 | from the technology-specific code that implements it, insulating the
221 | core of the application from technology.
222 |
223 | The domain object model of an _openMDX_-based application
224 | is specified by a platform-independent model (_PIM_). The model does not
225 | contain any platform-specific (_J2EE_, _WSDL_, _CORBA_,
226 | _RPC_, etc.) information. Using the _MDA/PIM_-approach
227 | has the following advantages:
228 |
229 | * __Automation__: the _PIM_ can be mapped by generators to any
230 | platform-specific artifact by applying the mappings specified by
231 | the _MDA_ standards. E.g. the _PIM_ can be mapped to a
232 | Java API by applying the _JMI_ mapping (specified by the [http://java.sun.com/products/jmi/index.jsp Java metadata interface (JMI)]
233 | standard).
234 | * __Single-source__: the domain model has to be specified exactly once
235 | even if the application must support several platforms, e.g. expose
236 | the application logic as Java library, _REST_ or _RPC_ service.
237 | * __Model-driven algorithms__: According to the _MDA_ standards, _PIMs_
238 | are stored in a _MOF (Meta Object Facility)_ repository which is
239 | accessible to the application at runtime. This allows an
240 | application to inspect the model and perform generic, model-driven
241 | algorithms on application data.
242 |
243 | The development process is rather straight-forward: first the business objects (BOs) are
244 | specified by platform-independent, _MOF_-compliant class diagrams. These are then mapped by
245 | _openMDX_-generators to Java interfaces by applying the _MOF_ to Java mapping
246 | (specified by the [http://java.sun.com/products/jmi/index.jsp Java metadata interface (JMI)]
247 | standard). These interfaces define the API of the the application's domain object model.
248 |
249 | 
250 |
251 | The corresponding _JMI_ interface is shown below:
252 |
253 | ~~~~~~
254 | :::java
255 | package org.openmdx.example.helloworld1.jmi1;
256 |
257 | public interface HelloWorld
258 | extends org.openmdx.example.helloworld1.cci2.HelloWorld,
259 | org.openmdx.base.jmi1.Segment{
260 |
261 | public org.openmdx.example.helloworld1.jmi1.SayHelloResult sayHello(
262 | org.openmdx.example.helloworld1.jmi1.HelloWorldSayHelloParams in
263 | );
264 |
265 | }
266 | ~~~~~~
267 |
268 | A sample hello world client:
269 |
270 | ~~~~~~
271 | :::java
272 | import org.openmdx.example.helloworld1.*;
273 |
274 | javax.jdo.PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("EntityManagerFactory");
275 | javax.jdo.PersistenceManager pm = pmf.getPersistenceManager("guest", "guest");
276 | HelloWorld helloWorld = (HelloWorld)pm.getObjectById(HELLOWORLD_XRI);
277 | HelloWorldPackage helloWorldPkg = (HelloWorldPackage)helloWorld.refOutermostPackage().refPackage(Helloworld1Package.class.getName());
278 | pm.currentTransaction().begin();
279 | org.openmdx.example.helloworld1.SayHelloResult hResult = helloWorld.sayHello(
280 | helloWorldPkg.createHelloWorldSayHelloParams("en")
281 | );
282 | pm.currentTransaction().commit();
283 | System.out.println("Client: sayHello[en]=" + hResult.getMessage());
284 | ~~~~~~
285 |
286 | The hello world implementation:
287 |
288 | ~~~~~~
289 | :::java
290 | package org.openmdx.example.helloworld1.aop2;
291 |
292 | import javax.jmi.reflect.RefException;
293 |
294 | import org.openmdx.base.aop2.AbstractObject;
295 | import org.openmdx.example.helloworld1.jmi1.HelloWorldSayHelloParams;
296 | import org.openmdx.example.helloworld1.jmi1.Helloworld1Package;
297 | import org.openmdx.example.helloworld1.jmi1.SayHelloResult;
298 |
299 | public class HelloWorldImpl
300 |
301 | extends AbstractObject {
302 |
303 | public HelloWorldImpl(
304 | S same,
305 | N next
306 | ) {
307 | super(same, next);
308 | System.out.println("Plugin: instantiating HelloWorldImpl");
309 | }
310 |
311 | public SayHelloResult sayHello(
312 | HelloWorldSayHelloParams params
313 | ) throws RefException {
314 | System.out.println("Plugin: invoking sayHello(language=" + params.getLanguage() + ")");
315 | String language = params.getLanguage();
316 | String message = null;
317 | if("de".equals(language)) {
318 | message = "hallo welt";
319 | }
320 | else if("fr".equals(language)) {
321 | message = "bonjour monde";
322 | }
323 | else {
324 | message = "hello world";
325 | }
326 | return ((Helloworld1Package)this.sameObject().refOutermostPackage().refPackage(
327 | Helloworld1Package.class.getName())
328 | ).createSayHelloResult(message);
329 | }
330 |
331 | }
332 | ~~~~~~
333 |
334 | ## The openMDX persistence manager ##
335 | In detail, the _openMDX_ persistence manager works as follows:
336 |
337 | * At the top level there are one or more _entity managers_.
338 | Their responsibility is to dispatch API method invocations issued
339 | by the client application and dispatch them to the configured AOPs.
340 | The last entity manager in the stack delegates to the
341 | object view manager which manages service data objects (SDOs).
342 | SDOs implement a generic interface which allows to manage the
343 | object's state in a generic way, e.g. objSetValue(feature, value)
344 | or _objGetValue(feature)_. For more information about SDOs see
345 | [http://www.ibm.com/developerworks/java/library/j-sdo/ Introduction to Service Data Objects].
346 | (__NOTE:__ openMDX SDOs implement the interface _DataObject_1_0_
347 | which is a much simpler and more JDO- and JPA-aligned than
348 | originally specified by IBM). Typed JMI method invocations are
349 | mapped to the SDO interface. E.g. if the client application invokes
350 | the method Person.setName(String name), the JMI persistence manager
351 | first checks if any AOP implements the method. If an implementation
352 | is found, the call is dispatched to this AOP. If none is found, the
353 | call is forwarded to the corresponding SDO and mapped to the method
354 | _objSetValue("name", value)_.
355 | * The _object view manager_ manages SDOs. It does it more or less the
356 | same way as the _JMI_ entity manager, however it allows to implement
357 | low-level, generic plug-ins. It also allows to register AOPs and
358 | dispatches method invocations. In order to be able to distinguish
359 | plug-ins at the different levels, plug-ins at JMI level are called
360 | AOP2, whereas plug-ins at object view level are called AOP1.
361 | The object view manager delegates to the next entity manager in
362 | the stack which is the data object manager.
363 | * The _data object manager_ manages transactions (also called
364 | unit of work), the state and the life-cycle of SDOs. The data
365 | object manager is either delegates to a local or remote _JCA_
366 | connection. All object operations are mapped to _REST_ interactions.
367 |
368 | # Summary #
369 | _openMDX_ helps you in writing platform-independent applications. The
370 | application's interface is specified with a platform independent model
371 | (_PIM_) and the business logic is implemented as POJOs (plain old Java
372 | objects) which are platform-, distribution- and persistence-technology
373 | independent. The application can be deployed locally or distributed by
374 | using the _RESTful_, _JDO_-compliant _openMDX_ persistence
375 | manager. The resulting applications are lightweight and can be deployed
376 | on any _J2SE_ or _J2EE_ platform such as _Tomcat/OpenEJB_.
377 |
--------------------------------------------------------------------------------
/Modeling/README.md:
--------------------------------------------------------------------------------
1 | # Introduction to Modeling with openMDX #
2 |
3 | * [Modeling Guide Part I](./files/openMDX-Modeling-Guide-Part1.pdf)
4 | * [Modeling Guide Part II](./files/openMDX-Modeling-Guide-Part2.pdf)
5 |
--------------------------------------------------------------------------------
/Modeling/files/openMDX-Modeling-Guide-Part1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Modeling/files/openMDX-Modeling-Guide-Part1.pdf
--------------------------------------------------------------------------------
/Modeling/files/openMDX-Modeling-Guide-Part2.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Modeling/files/openMDX-Modeling-Guide-Part2.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to openMDX
2 |
3 | openMDX is the leading open source MDA platform based on the [Object Management Group’s Model Driven Architecture](http://www.omg.org/mda/) standards. openMDX enables software architects and developers to build and integrate software applications in a highly automated and industrialized way. The main features are:
4 |
5 | * MDA-based development for Java.
6 | * PIM to Java API and JPA-ORM mapping.
7 | * No need to deal with platform-specific APIs such as J2EE, WebServices, ...
8 | * Highly customizable, model-driven GUI.
9 |
10 | On this wiki you find the latest information about _openMDX SDK_. You are welcome to add your requests, comments and contributions:
11 |
12 | * [Introduction to openMDX](./Introduction.md)
13 | * [Modeling with openMDX](./Modeling/README.md)
14 | * [openMDX SDK](./Sdk/README.md)
15 |
16 | For more information go to the [openMDX web site](http://www.openmdx.org).
17 |
--------------------------------------------------------------------------------
/Sdk/BuildFromSource.md:
--------------------------------------------------------------------------------
1 | # Build from source #
2 |
3 | This guide explains how to build _openMDX_ from the sources.
4 |
5 | ## Prerequisites ##
6 |
7 | Make sure that you have the following software installed:
8 |
9 | * [JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/) or [OpenJDK 8](https://openjdk.java.net/projects/jdk/8/)
10 | * [JDK 11](http://www.oracle.com/technetwork/java/javase/downloads/) or [OpenJDK 11](https://openjdk.java.net/projects/jdk/11/)
11 | * Set the environment variable JRE\_18 so that it points to the _jre_ directory of the JDK 8 installation, e.g. _/usr/lib/jvm/java-8-openjdk-amd64/jre_
12 | * Set the path to JDK\_11
13 | * [Gradle 6.9](https://gradle.org/install/)
14 | * [GIT](http://git-scm.com/downloads)
15 |
16 | ## Build ##
17 |
18 | Clone the openMDX sources from the GIT repository:
19 |
20 | ```
21 | git clone https://github.com/openmdx/openmdx.git openmdx
22 | ```
23 |
24 | And then build _openMDX_.
25 |
26 | ```
27 | cd openmdx
28 | git checkout openmdx-v2.17.10
29 | gradle clean
30 | gradle assemble
31 | ```
32 |
33 | ## Eclipse project files ##
34 |
35 | Generate the Eclipse project files as follows:
36 |
37 | ```
38 | gradle eclipse
39 | ```
40 |
41 | This generates the Eclipse project files for the sub-projects _client_, _core_, _portal_, _security_, _tomcat_. Import the projects into a new or existing Eclipse workspace.
42 |
43 | ## Congratulations ##
44 | Congratulations! You have successfully built _openMDX_ from the sources.
45 |
--------------------------------------------------------------------------------
/Sdk/Examples.md:
--------------------------------------------------------------------------------
1 | # openMDX Examples #
2 |
3 | This guide explains how to build the _openMDX Examples_.
4 |
5 | ## Prerequisites ##
6 |
7 | Make sure that you have the following software installed:
8 |
9 | * [JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/) or [OpenJDK 8](https://openjdk.java.net/projects/jdk/8/)
10 | * [JDK 11](http://www.oracle.com/technetwork/java/javase/downloads/) or [OpenJDK 11](https://openjdk.java.net/projects/jdk/11/)
11 | * Set the environment variable JRE\_18 so that it points to the _jre_ directory of the JDK 8 installation, e.g. _/usr/lib/jvm/java-8-openjdk-amd64/jre_
12 | * Set the path to JDK\_11
13 | * [Gradle 6.9](https://gradle.org/install/)
14 | * [GIT](http://git-scm.com/downloads)
15 |
16 | ## Build ##
17 |
18 | Get the _openMDX Examples_ sources from the GIT repository:
19 |
20 | ~~~~~~
21 | git clone https://github.com/openmdx/openmdx-example.git openmdx-example
22 | ~~~~~~
23 |
24 | And then build _openMDX Examples_.
25 |
26 | ~~~~~~
27 | cd openmdx-example
28 | git checkout openmdx-v2.17.10
29 | gradle clean
30 | gradle assemble
31 | ~~~~~~
32 |
33 | ## Eclipse project files ##
34 |
35 | Generate the Eclipse project files as follows:
36 |
37 | ```
38 | gradle eclipse
39 | ```
40 |
41 | This generates the Eclipse project files for the sub-projects _helloworld_, _workshop_. Import the projects into a new or existing Eclipse workspace.
42 |
43 | ## Running the JUnits ##
44 |
45 | Now you are ready to run the JUnit tests:
46 |
47 | ~~~~~~
48 | gradle test
49 | ~~~~~~
50 |
51 | ~~~~~~
52 | > Task :helloworld:test
53 |
54 | TestHelloWorld_1 > testHelloWorld() PASSED
55 |
56 | > Task :workshop:test
57 |
58 | TestWorkshop_1 > testVolatile() PASSED
59 |
60 | TestWorkshop_1 > testStandard() PASSED
61 |
62 | BUILD SUCCESSFUL in 3s
63 | 10 actionable tasks: 4 executed, 6 up-to-date
64 | ~~~~~~
65 |
66 | ## Congratulations ##
67 | Congratulations! You have successfully built and run the _openMDX Examples_.
68 |
--------------------------------------------------------------------------------
/Sdk/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to openMDX SDK #
2 |
3 | ## [openMDX Build from source](./BuildFromSource.md) ##
4 | This guide explains how to build openMDX from source.
5 |
6 | ## [openMDX Examples](./Examples.md) ##
7 | This guide explains how to install and build the openMDX Examples.
8 |
9 | ## [openMDX Workshop Project](./Workshop.md) ##
10 | This guide explains in detail the _openMDX Workshop_ project.
11 |
--------------------------------------------------------------------------------
/Sdk/Workshop.md:
--------------------------------------------------------------------------------
1 | # openMDX Workshop Project #
2 |
3 | This guide explains how to write applications using _openMDX_ based on the _openMDX/Examples Workshop_ project.
4 |
5 | ## Prerequisites ##
6 |
7 | This guide assumes that
8 |
9 | * The _openMDX SDK Examples_ are prepared as described in [openMDX Examples](./Examples.md)
10 | * You have read [Introduction to openMDX](../Introduction.md) and [Modeling with openMDX](../Modeling/README.md)
11 |
12 | ## Overview ##
13 |
14 | _openMDX_ is an _MDA_-based framework so the model plays a central role in the development process of
15 | _openMDX_-based applications. For more information about _MDA_ see [OMG Model Driven Architecture](http://www.omg.org/mda/).
16 | This guide covers the following aspects which are important when developing _openMDX_ based applications:
17 |
18 | * __Model__: Unlike with non-_MDA_ frameworks, the application's _API_ is specified with a _UML_ class model.
19 | An _openMDX_-specific mapper maps the model to Java APIs and other artifacts, which are then used by the
20 | programmer at development time and by _openMDX_ at runtime.
21 | * __Business logic__: The business logic is implemented as Java classes, which implement the
22 | generated APIs. Only the non-persistent features (or behavioral features) such as operations,
23 | derived attributes, shared associations and validations must be implemented by the programmer.
24 | Persistent features are handled by the _openMDX_ persistence manager.
25 | * __Persistence__: The mapping generates _JPA_ classes which are used to create the application's database
26 | schema. However, at runtime they are not used by _openMDX_. Instead, _openMDX_ comes with a generic,
27 | model-driven persistence manager which is used to retrieve and store objects on the database. This
28 | lightweight implementation allows _openMDX_ applications to be run on devices with limited memory and
29 | CPU resources.
30 |
31 | ## The Model ##
32 | As already mentioned, the application's _API_ is specified as UML class diagram. The model plays a central
33 | role in _openMDX_ based applications. It is used used during development and runtime.
34 |
35 | ### Development ###
36 | The model specifies the _API_ of an application. It contains all business objects, their attributes and
37 | associations. _openMDX_ comes with a model mapping utility which automatically generates the following artifacts
38 | based on the model:
39 |
40 | * __Java interfaces__: The model is mapped to Java interfaces according to the mapping specified
41 | by the _JMI_ standard (For more information see [The Java Metadata Interface (JMI) Specification](http://jcp.org/en/jsr/detail?id=40).
42 | After running _gradle assemble_ you will find these files in the _./build/generated/sources/java/main_. The
43 | model mapper generates _JavaBeans_ compliant interfaces with package-suffix _cci2_ and _JMI_-compliant
44 | interfaces with package-suffix _jmi1_. These interfaces define the _API_ of the application. Object
45 | instances can be managed at runtime using the _openMDX_ _JDO_-compliant persistence manager or the _REST_
46 | service. The class _org.openmdx.test.example.workshop1.TestWorkshop\_1_ shows how to use the _Workshop_ _API_.
47 | * __JPA classes__: Although the model specifies the _API_ of an application, it is a quite common use case that
48 | the same objects have to be made persistent and stored as-is in a database. Although an _openMDX_-based
49 | application does not require objects to be persistent (objects may be fully transient), corresponding _JPA_ classes
50 | with package-suffix _jpa3_ are generated by the model mapper for convenience. In addition the corresponding
51 | _orm.xml_ is generated in _build/resources/main/META-INF/_. As explained later in this guide,
52 | the mapping process can be customized by supplying _JDO_ metadata information. For the _Workshop_ project these
53 | files are located in _src/main/resources/org/openmdx/example/workshop1/jpa3/_.
54 | * __Model XML files__: The mapper generates XML files for each model package. The model files contain the full
55 | model information such as classes, attributes and associations. These files are used at runtime to populate
56 | the _MOF_ repository (__Note__: technically the original XMI files could be used for this purpose. For
57 | implementation reasons, _openMDX_ uses a normalized model file format).
58 |
59 | All files generated by the mapper are stored in a _ZIP_ which is located in _build/generated/sources/model/openmdx-workshop-models.zip_.
60 | The build process extracts the files into the corresponding folders in the _build_ directory so that they are
61 | accessible by the _Java_ compiler and _Eclipse_.
62 |
63 | ### Runtime ###
64 | A model repository gives access to the model information at runtime. The model repository is _MOF1_-compliant.
65 | It is used by the _openMDX_ runtime but can also be used by applications. The repository is populated
66 | according to the model packages listed in the _/META-INF/openmdxmof.properties_ files found on the classpath.
67 | The model is validated at load-time for consistency. _openMDX_ uses the model repository at runtime for the
68 | following purposes:
69 |
70 | * __Persistence manager__: Based on the model and additional database mapping information, the persistence
71 | manager is able to store objects to and retrieve from any _SQL_-compliant database. The persistence manager
72 | itself does not use the generated _JPA_ classes and also is not based on another persistence framework. The
73 | persistence manager's data access layer is a generic, model-driven algorithm which dynamically generates the
74 | required _SQL_ statements (for implementation details see _org.openmdx.base.dataprovider.layer.persistence.jdbc.Database\_2_).
75 | * __Invoking business logic__: As already mentioned, the business logic is added as Java classes. The
76 | corresponding implementation classes must be registered with the persistence manager. Every time a
77 | feature (operation, attribute, association) is accessed on the _API_, the persistence manager checks whether
78 | this feature is provided by an implementation class. If yes, the invocation is intercepted and dispatched
79 | to the implementation class. If no, the access is delegated to the data access layer. This dispatching is
80 | implemented in a generic way using the _Java_ proxy feature and requires access to the model information.
81 | * __REST access__: The _REST_ servlet allows _REST_-based access to the application's _API_. This allows easy
82 | access to the _API_ for 3rd party applications. Moreover, _REST_ is also used as persistence manager internal
83 | protocol in case of distributed _openMDX_ applications.
84 | * __Generic GUI__: _openMDX_ comes with a _GUI_ which is a generic, model-driven _Java_ servlet. The model
85 | information and additional, manual-written customizing meta data allows the servlet to dynamically render
86 | a web-based _GUI_ which gives full access to all _API_ features.
87 |
88 | ## Business Logic ##
89 | The business logic is implemented as standard Java classes (and methods). Adding implementation classes
90 | requires the following steps:
91 |
92 | * __Determine features to be implemented__: Inspect the model (or alternatively the generated Java _API_)
93 | and determine features which you want to implement. Features which must be implemented are: operations,
94 | derived attributes, derived references, shared associations. By default all other features are handled
95 | by the persistence manager: a corresponding feature access is delegated 1:1 to the persistence layer
96 | and handled by the database.
97 | * __Implement features__: Implement the features by creating a Java class which corresponds to the model
98 | class name and implements the feature as method. E.g. in the _Workshop_ project the class
99 | _org::openmdx::example::workshop1::Task_ defines the operation _assignTeamMember_. This operation is
100 | implemented by the _Java_ class _org.openmdx.example.workshop1.aop2.TaskImpl_ and operation _assignTeamMember_.
101 | Shared associations can be either implemented in Java or at persistence-level by the means of database views.
102 | The _Workshop_ project has one shared association: _TeamMemberHasTask_. This is implemented by the manual written
103 | view _OOMEP1WS\_JOIN\_TMEMBERHASTASK_ which is defined in _./src/sql/hsqldb-2/dbcreate\_views.sql_.
104 | * __Register features__: The implemented business logic must be configured in the persistence manager's configuration
105 | file _jdoconfig.xml_ which is located in _./src/main/resources/META-INF_. This is the main configuration file
106 | which in turn may reference additional configuration files. For the _Workshop_ project these are:
107 | * _WorkshopStandard.properties_: Configuration using database persistence
108 | * _WorkshopVolatile.properties_: Configuration using in-memory persistence
109 |
110 | The Java classes are registered with the option _packageImpl_ in _jdoconfig.xml_. This option specifies the
111 | Java packages containing the implementation classes. The database view which implements the shared association
112 | _TeamMemberHasTask_ is configured in _WorkshopStandard.properties_.
113 |
114 | ### Persistence ###
115 | The _.openmdxjdo_ meta data files allow to customize the mapping of model classes to _JPA_ classes
116 | and the _orm.xml_ which are generated by the _openMDX_ model mapper. An _.openmdxjdo_ file must be provided
117 | for each modeled type (a class referenced by composite association) and are optional for all other classes. The
118 | _.openmdxjdo_ files allow to
119 |
120 | * Map model classes to database tables
121 | * Map attributes to column names
122 | * Specify name of join tables in case of shared associations
123 | * Specify non-persistent features
124 |
125 | For the _Workshop_ project the files are located in _./src/main/resources/org/openmdx/example/workshop1/jpa3_. The
126 | generated _JPA_ classes and _orm.xml_ are _JPA_-compliant and may be used by any _JPA_-compliant implementation.
127 | _openMDX_ uses _OpenJPA_ during the build process for the purposes:
128 |
129 | * gradle task _create\_schema_: Given the _orm.xml_ and the _JPA_ classes a database-independent schema XML is generated
130 | using the _OpenJPA_ schema tool.
131 | * gradle task _create\_sql_: Given the schema _XML_ and the _JPA_ classes, a database-specific schema script is generated
132 | using the _OpenJPA_ scripting tool. This script can finally be used to setup the database.
133 |
134 | As already mentioned, the _openMDX_ persistence manager provides its own database access. A 3rd party persistence
135 | framework is not required at runtime.
136 |
137 | The following figure gives an overview of this process.
138 |
139 | 
140 |
141 | ## Development Roundtrip ##
142 | This section a full development cycle, starting from the model up the the final deployable application.
143 | This section is based on the _Workshop_ project.
144 |
145 | ### Project Structure ###
146 | The standard project layout looks as follows:
147 |
148 | ~~~~~~
149 | -build.gradle.kts
150 | +etc
151 | +openjpa
152 | -hsqldb-2-persistence.xml # Connector used by tasks create-schema, create-sql
153 | +src
154 | +main/java # Java sources
155 | +main/resources # Resources such as jdoconfig.xml, .openmdxjdo mappings, ...
156 | +model # Model sources
157 | +test/java # Java test sources
158 | ~~~~~~
159 |
160 | ### Create the Model ###
161 | _openMDX_ requires that the class model is _MOF1_ compliant, i.e. use _MOF1_ model elements only.
162 | The restriction to _MOF_ model elements has the advantage that it is possible to apply standard
163 | _MOF_ mappings (e.g. _JMI_, _XMI_) to models and not just meta models. On the other side,
164 | the restriction to _MOF_ model elements does not have a big impact on the modeler. Features
165 | such as association classes and class dependencies are typically not required for application-level
166 | modeling and if so, these features can be modeled in alternate ways. One exception are
167 | qualifiers: in addition to the _MOF_ model elements, _openMDX_ supports _UML qualifiers_. Qualifiers
168 | are required to specify in a precise way the object access paths. For more information about
169 | modeling see [Modeling with openMDX](../Modeling/README.md).
170 |
171 | The figure below shows the _Workshop_ model.
172 |
173 | 
174 |
175 | __NOTE:__ All modeled classes inherit from the class _org::openmdx::base::BasicObject_
176 | (not shown). Extending from _BasicObject_ is not strictly required by the _openMDX_
177 | persistence manager. However, it is good practice. _BasicObject_ defines the
178 | attributes _createdAt_, _createdBy_, _modifiedAt_, _modifiedBy_ which should be
179 | available for all business objects.
180 |
181 | We recommend using _Eclipse Papyrus_ for model editing. The following modeling tools
182 | are currently supported:
183 | * EMF: _Eclipse Papyrus_
184 | * RSM: _IBM Rational Software Modeler_
185 |
186 | Put the model files in the model tool specific sub-directory and instruct the model mapper
187 | which model files to use in _build.properties_:
188 |
189 | ~~~~~~
190 | # Use EMF. Files are in ./src/model/emf
191 | model.transformation.source=emf
192 |
193 | # Use RSM. Files are in ./src/model/rsm
194 | # model.transformation.source=rsm
195 | ~~~~~~
196 |
197 | __NOTE__: Using _EMF_ and _Papyrus_ requires some experience. In order to create a
198 | model for a new project, it is a good idea to start with the model file from the _Workshop_
199 | project. In a second step adapt the model package names and then add and remove classes and
200 | associations to your needs.
201 |
202 | As already explained, the model specifies the application's API. The model is mapped to
203 | _Java_ interfaces. Clients use this API to access the application's business logic. This
204 | mapping implies that the API exposes a fine-granular object model. Unlike with typical
205 | web service APIs, a client does not invoke services by passing input parameters and
206 | receiving results. Instead, a client retrieves, queries, creates, updates and deletes
207 | objects using the persistence manager's _API_. It is important to note that not each
208 | object and feature access necessarily leads to a service or database round-trip. The
209 | _openMDX_ persistence manager implements a _REST_ pattern at transport-level and offers
210 | many of the _JDO_ patterns which allow to minimize round-trips:
211 |
212 | * Objects are cached client-side and have well-defined _JDO_-states
213 | * Objects are retrieved with a default fetch set
214 | * Optimistic or non-optimistic units of work (load, edit, store), etc.
215 |
216 | This approach is a very different to the SOA approach as used by _WebServices_,
217 | _EJB_ or _CORBA_. _SOA_ requires to specify coarse-granular, service-oriented
218 | interfaces, whereas the _openMDX_- (or _REST_-) based approach allows to specify
219 | fine-granular, business-object oriented interfaces.
220 |
221 | ### Add Business Logic ###
222 | The _Workshop_ project comes with one implementation class _TaskImpl_. The class
223 | implements the operation _assignTeamMember_ which is defined on the model_ class
224 | _Task_. The implementation package must be registered in the persistence manager's
225 | configuration _jdoconfig.xml_ with the option _packageImpl_. Some facts are important
226 | to notice:
227 |
228 | * Implementation classes extend the abstract _AbstractObject_. This class has default
229 | implementations for _JDO_ callbacks and offers some helper methods. _AbstractObject_ allows
230 | the persistence manager to interact in a standard way with user-supplied implementations.
231 | * It is not required that the implementation classes implement the mode-derived interfaces,
232 | e.g. that _TaskImpl_ implements _org.openmdx.example.workshop1.jmi1.Task_. This way, only the
233 | methods with custom-specific business logic have to be implemented. Non-implemented features
234 | are not dispatched and handled by the persistence manager. The default behaviour is to
235 | forward the request to the next plug-in in the configured plug-in stack which is typically
236 | the data access layer (the plug-in stack is configured in _jdoconfig.xml_).
237 |
238 | ### Add Persistence ###
239 | Most of the persistence work is handled by the model-driven _openMDX_ persistence manager.
240 | However, a few steps involves manual work:
241 |
242 | * Create a database
243 | * Create the database schema
244 | * Configure the persistence manager
245 |
246 | The following databases are supported by the _openMDX_ persistence manager:
247 |
248 | * PostgreSQL
249 | * HSQLDB
250 | * MySQL
251 | * Microsoft SQL Server
252 | * Oracle
253 | * DB/2
254 |
255 | #### Create the database ####
256 | In the _Workshop_ example we use _HSQLDB_. There are scripts to start the database and
257 | launch the database manager. The scripts are located in the folder _etc/data/workshop/_.
258 | _./startdb.sh_ starts the database, _./startdbmanager.sh_ starts the database manager console.
259 | The _Workshop_ example comes with an initialized database containing sample data. If you
260 | want to start with an empty database, clear the content of the file _etc/data/workshop/workshop.script_
261 | and restart _HSQLDB_.
262 |
263 | #### Create the database schema ####
264 | The _openMDX_ build libraries provide gradle tasks which allow to create the database schema for
265 | a project. As described in the previous sections, the process to build a schema is as follows:
266 |
267 | * Running _gradle assemble_ generates a _JPA_-compliant _orm.xml_ which is stored in
268 | _build/resources/main/META-INF/orm.xml_.
269 | * Running _gradle -Pdatabase.name="hsqldb-2" create-schema"_ creates a database-independent schema
270 | file which is stored _build/generated/sources/sql/openjpa-schema.xml_.
271 | * Move the file _openjpa-schema.xml_ to the folder _src/sql/_. Open it in _Eclipse_. Remove all
272 | table definitions which are not prefixed with _OOMEP1WS_. Also remove the table definition
273 | _OOMEP1WS\_JOIN\_TMEMBERHASTASK_. This object will be implemented by a manual-written view.
274 | * Running _gradle -Pdatabase.name="hsqldb-2" create-sql_ generates the schema scripts for _HSQLDB_.
275 | The output goes to _build/generated/sources/sql/_.
276 | * Execute the script _hsqldb-2-build.sql_ in the _HSQLDB_ database manager console. This creates
277 | the schema for the _Workshop_ database.
278 |
279 | ### Building and Running ###
280 | You are now ready to build and run the project.
281 |
282 | ~~~~~~
283 | gradle clean
284 | gradle assemble
285 | gradle test
286 | ~~~~~~
287 |
288 | A _Junit_ test running _TestExample\_1_ in console mode. The _Junit_ prepares the _JNDI_ by adding
289 | _JDBC_ connector entry for the _Workshop_ database. This allows to launch and run the _openMDX_ persistence
290 | manager on a standard _JVM_ without using the services of an application server.
291 |
292 | ~~~~~~
293 | gradle test
294 |
295 | > Task :workshop:test
296 |
297 | TestWorkshop_1 > testVolatile() PASSED
298 |
299 | TestWorkshop_1 > testStandard() PASSED
300 |
301 | BUILD SUCCESSFUL in 2s
302 | 5 actionable tasks: 1 executed, 4 up-to-date
303 | ~~~~~~
304 |
305 | ### Deploy as REST service ###
306 | The workshop application can be deployed as REST service. For this purpose the gradle task _assemble_
307 | has built the WAR _openmdx-workshop-rest.war_ which is located in _./build/deployment-unit/_.
308 |
309 | The WAR can be deployed on _Apache TomEE Plus 8.0.6_ as follows:
310 |
311 | * Download _Apache TomEE Plus 8.0.6_ from [here](https://tomee.apache.org/download.html#tomee-8.0)
312 | * Copy _openmdx-workshop-rest.war_ to _apache-tomee-plus-8.0.3/webapps/_
313 | * Upgrade the JDBC driver for HSQDB: copy _hsqldb-2.5.0.jar_ to _apache-tomee-plus-8.0.3/lib/_ and remove _hsqldb-2.3.2.jar_.
314 | **NOTE:** the version of the driver must match with the version that was specified when launching the _WORKSHOP_ database with the startup
315 | script located in _./etc/data/workshop/_
316 | * In _./conf/tomee.xml_ add the following JDBC datasource:
317 |
318 | ~~~~~~
319 |
320 | JdbcDriver org.hsqldb.jdbcDriver
321 | JdbcUrl jdbc:hsqldb:hsql://127.0.0.1:9002/WORKSHOP
322 | UserName sa
323 | Password manager99
324 | JtaManaged true
325 |
326 | ~~~~~~
327 |
328 | * In _./conf/tomcat-users.xml_ add the user _guest_:
329 |
330 | ~~~~~~
331 |
332 | ~~~~~~
333 |
334 | * Start _TomEE Plus_ on the command-line with _./bin/catalina.sh_. If properly configured and deployed the log looks as follows:
335 |
336 | ~~~~~~
337 | ...
338 | INFO [main] jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke Deploying web application archive [/opt/apache-tomee-plus-8.0.3/webapps/openmdx-workshop-rest.war]
339 | INFO [main] org.apache.tomee.catalina.TomcatWebAppBuilder.init ------------------------- localhost -> /openmdx-workshop-rest
340 | INFO [main] org.apache.openejb.config.ConfigurationFactory.configureApplication Configuring enterprise application: /opt/apache-tomee-plus-8.0.3/webapps/openmdx-workshop-rest
341 | INFO [main] org.apache.openejb.config.AutoConfig.processResourceRef Auto-linking resource-ref 'jdbc/jdbc_openmdx_example_workshop' in bean openmdx-workshop-rest.Comp1958403865 to Resource(id=jdbc/jdbc_openmdx_example_workshop)
342 | INFO [main] org.apache.openejb.config.AppInfoBuilder.build Enterprise application "/opt/apache-tomee-plus-8.0.3/webapps/openmdx-workshop-rest" loaded.
343 | INFO [main] org.apache.openejb.assembler.classic.Assembler.createApplication Assembling app: /opt/apache-tomee-plus-8.0.3/webapps/openmdx-workshop-rest
344 | INFO [main] org.apache.tomee.catalina.TomcatWebAppBuilder.deployWebApps using context file /opt/apache-tomee-plus-8.0.3/webapps/openmdx-workshop-rest/META-INF/context.xml
345 | INFO [main] org.apache.openejb.assembler.classic.Assembler.createApplication Deployed Application(path=/opt/apache-tomee-plus-8.0.3/webapps/openmdx-workshop-rest)
346 | INFO [main] org.apache.myfaces.ee.MyFacesContainerInitializer.onStartup Using org.apache.myfaces.ee.MyFacesContainerInitializer
347 | INFO [main] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
348 | INFO [main] jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke Deployment of web application archive [/opt/apache-tomee-plus-8.0.3/webapps/openmdx-workshop-rest.war] has finished in [113] ms
349 | ...
350 | ~~~~~~
351 |
352 | * Launch Swagger with the following URL and explore the API:
353 |
354 | ~~~~~~
355 | http://localhost:8080/openmdx-workshop-rest/api-ui/index.html?url=http://localhost:8080/openmdx-workshop-rest/org.openmdx.example.workshop1/provider/Workshop/segment/Standard/:api
356 | ~~~~~~
357 |
358 | 
359 |
360 | ## Congratulations ##
361 | Congratulations! You have successfully completed the _openMDX Workshop Project_ guide.
362 |
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p007.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p007.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p108.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p108.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p110.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p110.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p111.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p115.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p115.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p120.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p125.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p130.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p130.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p140.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p140.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p150.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p160.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p160.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p170.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p170.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p180.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p190.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p190.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p200.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p210.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p210.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p220.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p220.png
--------------------------------------------------------------------------------
/Sdk/files/Examples/Examples.p230.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Examples/Examples.p230.png
--------------------------------------------------------------------------------
/Sdk/files/Workshop/Workshop.pic010.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Workshop/Workshop.pic010.png
--------------------------------------------------------------------------------
/Sdk/files/Workshop/Workshop.pic020.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Workshop/Workshop.pic020.png
--------------------------------------------------------------------------------
/Sdk/files/Workshop/Workshop.pic030.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/Sdk/files/Workshop/Workshop.pic030.png
--------------------------------------------------------------------------------
/files/Overview.p010.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/files/Overview.p010.png
--------------------------------------------------------------------------------
/files/Overview.p020.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openmdx/openmdx-documentation/2de63a5a9c7fe89a103c85425da0280d812d6a69/files/Overview.p020.png
--------------------------------------------------------------------------------