├── .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 | ![img](files/Overview.p010.png) 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 | ![img](files/Workshop/Workshop.pic010.png) 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 | ![img](files/Workshop/Workshop.pic020.png) 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 | ![img](files/Workshop/Workshop.pic030.png) 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 --------------------------------------------------------------------------------