Before you can install and build the application, you have to checkout and install: (gradlew install)
MDM 5 backend implements the delegated approach to roles and permissions, wherein the data sources (ASAM ODS server, PAK cloud) themselves can implement their own security scheme (which they already have) and then delegates the appropriate user data to the backends.
In the case of ASAM ODS servers this is done using a technical user and the for_user
attribute on the established connection, so it is a form of ‘login on behalf’.
In the case of PAK Cloud this is done by passing the user name along with a http header X-Remote-User
which is then used by PAK Cloud to establish the users roles (from an internal database or an external authentication provider).
Before the user is ‘logged in on behalf’ he is authenticated by a LoginModule within the Glassfish application server. There are different implementations available (e.g. LDAP, Certificate, JDBC, ...). To keep this guide simple, we will setup a FileRealm, which stores the user information in a flat file:
The following command will create a FileRealm with name MDMRealm
that stores the users in a file called mdm-keyfile
:
asadmin create-auth-realm --classname com.sun.enterprise.security.auth.realm.file.FileRealm --property file=${com.sun.aas.instanceRoot}/config/mdm-keyfile:jaas-context=MDMRealm:assign-groups=MDM MDMRealm
To be able to login you need to explicitly add users to the MDMRealm
:
asadmin create-file-user --authrealmname MDMRealm
Next you need to add the following snippet to your login.conf
in ${com.sun.aas.instanceRoot}/config/
MDMRealm { com.sun.enterprise.security.auth.login.FileLoginModule required; };
As a last step you have to provide the credentials of the technical user in adapter configuration in service.xml
. For the ODS adapter you you have to specify the parameters user
and password
of the technical user. For example:
<service entityManagerFactoryClass="org.eclipse.mdm.api.odsadapter.ODSContextFactory"> ... <param name="user">sa</param> <param name="password">sa</param> ... </service>
Make sure to restart Glassfish after this change.
In versions 5.0.0M1, 0.10 and older the configuration of a custom login realm was neccessary. If you configured your glassfish instance for one of these version, you can remove the old configuration options and artifact, as they are no longer needed:
delete the jar file org.eclipse.mdm.realm.login.glassfish-VERSION.jar from GLASSFISH_ROOT/glassfish/domains/domain1/lib
open the Glassfish login configuration file at GLASSFISH_ROOT/glassfish/domains/domain1/config/login.conf
delete custom MDM realm module entry to this config file
MDMLoginRealm { org.eclipse.mdm.realm.login.glassfish.LoginRealmModule required; };
asadmin delete-auth-realm MDMLoginRealm
MDM 5 uses SLF4J and logback for logging. The default configuration file can be found at org.eclipse.mdm.nucleus/src/main/resources/logback.xml. It logs INFO level messages to mdm5.log in the logs folder of the Glassfish domain. If you want to customize logging, you can either edit the file within the war file or preferably provide your own logging configuration via system parameter in the JVM settings in Glassfish: -Dlogback.configurationFile=/path/to/config.xml
Please use the generated OpenAPI Specification, which is generated at build time. The OpenAPI Specification is available in org.eclipse.mdm.nucleus/build/openapi/openapi.json
or at runtime at http://{SERVER}:{PORT}/{APPLICATIONROOT}/openapi.json
. Futhermore a Swagger UI is available under http://{SERVER}:{PORT}/{APPLICATIONROOT}/swagger.html
.
The following list of URLs will be removed in future releases.
Business Object: Environment
Since all other calls operate on a specific source all subsequent calls share the common prefix http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/
, which we will be our root for the description of the next calls. Strings enclosed in curly brackets are meant to be replaced by appropriate values. The following parameters are recurring throughout the different URLs:
Test.Name eq "t*"
filters for all tests which names begin with t
. Strings should be quoted with "
. "
characters within strings have to be escaped with a backslash. For backward compatibility '
is also allowed to quote strings, but may be blocked in URLs in some environments, thus "
should be preferred.%2F
.Business Object: Test
Business Object: TestStep
Business Object: Measurement
Business Object: ChannelGroup
Business Object: Channel
Business Object: Project
Business Object: Pool
Business Object: ValueList
Business Object: ValueListValue
Business Object: PhysicalDimension
Business Object: Unit
Business Object: Quantity
Business Object: CatalogComponent
Business Object: CatalogAttribute
Business Object: CatalogSensor
Business Object: CatalogSensorAttribute
Business Object: TemplateRoot
Business Object: TemplateComponent
Business Object: TemplateAttribute
Business Object: TemplateSensor
Business Object: TemplateSensorAttribute
Business Object: NestedTemplateComponent
Business Object: NestedTemplateAttribute
CONTEXTTYPE is one of [unitundertest, testsequence, testequipment]
GET: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs
POST: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs (JSON: { “Name” : “testCatalogAttribute” } (name must be identical with corresponding CatalogAttribute))
GET: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/{NESTEDTEMPLATEATTRIBUTEID}
PUT: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/{NESTEDTEMPLATEATTRIBUTEID} (JSON: { “MimeType” : “myMimeType” })
DELETE: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/{NESTEDTEMPLATEATTRIBUTEID}
GET: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/searchattributes
GET: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/localizations
Business Object: TemplateTest
Business Object: TemplateTestStep
Business Object: TemplateTestStepUsage
Values endpoint
Values endpoint accepts application/protobuf
and application/json
. For larger amounts of data Protobuf should be preferred. The IDL-file for Protobuf is available in org.eclipse.mdm.businessobjects/src/main/proto/mdm.proto
Query endpoint
http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/query
_example:curl -POST -H "Content-Type: application/json" -d '{"resultType": "test", "columns": ["Test.Name", "TestStep.Name"], "filters": { "sourceName": "SOURCENAME", "filter": "Test.Id gt 1", "searchString": ""}}'http://sa:sa@localhost:8080/org.eclipse.mdm.nucleus/mdm/query
http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/suggestions
_example: curl -POST -H "Content-Type: application/json" -d '{"sourceNames": ["SOURCENAME"], "type": "Test", "attrName": "Name"}' http://sa:sa@localhost:8080/org.eclipse.mdm.nucleus/mdm/suggestions
ATFX Import
POST: http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/import
Imports an ATFX file into the specified environment. The ATFX file must conform to the openMDM5 datamodel.
TODO Templates
Three content-types are accepted:
The POST request returns a JSON-Object with the following properties:
OK
or FAILED
.The returned HTTP status is either 200
, if the imported succeeded or 400
, if the import failed.
ATFX Export
POST: http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/export
Accepts a shopping basket XML file and exports the contained elements into an ATFX file. Currently it is only supported to export objects from one environment.
Returned content types in response:
###Example: file download
curl -XPOST --cookie-jar cookie -H "Content-Type: application/x-www-form-urlencoded" -d j_username=sa -d j_password=sa http://localhost:8080/org.eclipse.mdm.nucleus/j_security_check
FILE_PATH="$(curl --cookie cookie -s http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMNVH/teststeps/10 | jq -r '.data[0].attributes[] | select (.name | contains("MDMLinks")) | .value[0].remotePath')"
FILE_PATH="$(echo $FILE_PATH | python -c 'import sys,urllib;print urllib.quote(sys.stdin.read().strip(), "")')"
curl --cookie cookie http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMNVH/teststeps/10/files/$FILE_PATH
Preference service stores its data to a relational database. The database connection is looked up by JNDI and the JNDI name and other database relevant parameters are specified in src/main/resources/META-INF/persistence.xml. The default JNDI name for the JDBC resource is set to jdbc/openMDM. This JDBC resource and its dependent JDBC Connection Pool has to be created and configured within the glassfish web administration console or through asadmin command line tool.
Furthermore the schema has to be created in the configured database. Therefore database DDL scripts are available for PostgreSQL and Apache Derby databases in the folder schema/org.eclipse.mdm.preferences
of the distribution. Other databases supported by EclipseLink may also work, but is up to the user to adapt the DDL scripts.
curl -GET -H "Content-Type: application/json" http://localhost:8080/org.eclipse.mdm.nucleus/mdm/preferences?scope=SYSTEM&key=ignoredAttributes
curl -PUT -H "Content-Type: application/json" -d '{"scope": "SYSTEM", "key": "ignoredAttributes", "value": "[\"*.MimeType\"]"}' http://localhost:8080/org.eclipse.mdm.nucleus/mdm/preferences
curl -DELETE http://localhost:8080/org.eclipse.mdm.nucleus/mdm/preferences/ID
The Indexing is completely independent from the searching. So the Indexer can be freely deployed at any other machine. In the simplest case, the same steps as in Configuration have to be done. The application can then be deployed on any other machine. All components besides the FreeTextIndexer and its dependencies are not user. Those can be left out, if desired.
The service.xml contains all information necessary for the Connector-Service to connect to the available datasources/adapter instances. Since this information includes secret information like passwords, it is possible to provide lookups, which gives you the possibility to specify tokens as references to properties defined elsewhere.
There are different lookups available:
Example: <param name="password">${env:odsPassword}</param>
##Known issues: If you run into “java.lang.ClassNotFoundException: javax.xml.parsers.ParserConfigurationException not found by org.eclipse.persistence.moxy” this is a bug described in https://bugs.eclipse.org/bugs/show_bug.cgi?id=463169 and https://java.net/jira/browse/GLASSFISH-21440. This solution is to replace GLASSFISH_HOME/glassfish/modules/org.eclipse.persistence.moxy.jar with this: http://central.maven.org/maven2/org/eclipse/persistence/org.eclipse.persistence.moxy/2.6.1/org.eclipse.persistence.moxy-2.6.1.jar
If you run into “java.lang.ClassNotFoundException: com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector not found by com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider” you have to download http://central.maven.org/maven2/com/fasterxml/jackson/module/jackson-module-jaxb-annotations/2.5.1/jackson-module-jaxb-annotations-2.5.1.jar and put it under GLASSFISH_HOME/glassfish/domains/domain1/autodeploy/bundles
The applications preferences are managed in the administration section. This section can be accessed via the Administration
button in the main navigation bar or via
http://../administration
. A preference is a pair of a unique key and a value. The key is composed of a prefix defining the purpose of the preference followed by an arbitrary but unique identifier string. It is recommended to choose the identifier the same as the preferences ‘name’ field, in case there is one. The value holds the preference's data in a Json string. The following preferences, sorted by their scope, can be set:User:
System:
Source:
However, it might be necessary to reload the application before a newly defined preference is available or any changes on an existing preferences are applied. WARNING: Corrupted preferences can result in malfunctions of the application.
A user scoped preference's area of effect is limited to the logged in user. All user scoped preferences can also be set in dialogs in the main application.
1.) Basket Basket preferences keys must start with the prefix basket.nodes.
. This preference has the fields ‘items’ and ‘name’ and holds all the information for saved baskets. The field ‘items’ holds an array of MDMItems, providing the relevant information of a related node, i.e. ‘source’, ‘type’ and ‘id’. The field ‘name’ defines the name, which is provided in the main application to load this basket.
** Example: { “items”: [{“source”:“MDMNVH”,“type”:“Test”,“id”:38}], “name”: “basketExample” }
2.) View View preferences keys must start with the prefix tableview.view.
This preference has the fields ‘columns’ and ‘name’ and holds the layout information for the tables displaying the search results and the basket nodes. The field ‘columns’ holds an array of ViewColumn objects. A ViewColumn is an Object with the fields ‘type’, ‘name’, ‘sortOrder’ and an optional field ‘style’. The ViewColumn‘s ‘type’ can be set to all available MDM data types, i.e. Project
, Pool
, Test
, TestStep
, Measurement
, ChannelGroup
, Channel
. The ViewColumn’s ‘name’ field specifies an attribute, which must be an searchable attribute for the given ‘type’. The ViewColumn‘s sortOrder can be set by the number 1
(ascending), -1
(descending), or null (unsorted). Only one column of the array can have a non-null value sortOrder at a time. The ViewColumn’s style element can hold any CSS-style object. However, it is supposed to contain only the columns width. The column order in the array is identically with the appearance in the table. The view's field ‘name’ defines the name, which is provided in the main application to load this view.
**Example: { “columns”: [{“type”:“Test”,“name”:“Id”,“style”:{“width”:“75px”},“sortOrder”:null}], “name”: “viewExample” }
3.) Filter Filter preferences keys must start with the prefix filter.nodes.
. This preference has the fields ‘conditions’, ‘name’, ‘environments’, ‘resultType’ and ‘fulltextQuery’. It provides the information for the attribute based / advanced search. The field ‘conditions’ holds an array of Condition objects. A Condition specifies a search condition for attribute based search. It consists of the fields ‘type’, ‘name’, ‘operator’, ‘value’ and ‘valueType’. The Condition‘s ‘type’ can be set to all available MDM data types, i.e. Project
, Pool
, Test
, TestStep
, Measurement
, ChannelGroup
, Channel
. The Condition’s ‘name’ field specifies an attribute, which must be an searchable attribute for the given ‘type’. The Condition‘s ‘operator’ field, holds on of the following numbers: 0
(=), 1
(<), 2
(>), 3
(like). The Condition’s ‘value’ field holds a string array containing input for the attribute based search. The Condition‘s ‘resultType’ field should match the type corresponding to the attribute specified in the ‘name’ filed, e.g. string
, date
or long
. The Filter’s field ‘name’ defines the name, which is provided in the main application to load this filter. The Filter‘s field ‘environments’ holds an string array with the names of the sources that should be included in the search. The Filter’s field ‘resultType’ can be set to all available MDM data types (see above). Only nodes of this type will be included in the search. The Filter's field ‘fulltextQuery’ holds a string containing full text search input.
**Example: { “conditions”:[{“type”:“Test”,“attribute”:“Name”,“operator”:0,“value”:[],“valueType”:“string”}], “name”:“filterExample”, “environments”:[“sourceName”], “resultType”:“Test”, “fulltextQuery”:"" }
System scoped preference are applied globally.
1.) Node provider
The navigation tree structure can be defined via a node provider. The default node provider is set in
I.) Structure
a) First layer/root nodes In the node provider each layer of nodes of the navigation tree is defined in a nested object. The first layer of nodes, is always the environment level. This is necessary due to the provided endpoints. The first layer consists of the fields 'name', 'type', and 'children'. The field 'name' sets the name, which is displayed in the application to select the corresponding node provider. The field 'type' defines the data type of the nodes, which is always `Environments` on the first layer. The next layer of nodes are defined via the field 'children'. b) Children A child object consists of the fields 'type', 'query', and 'children'. The field 'type' sets the data type of this layer of nodes. It can be set to all available MDM data types, i.e. `Project`, `Pool`, `Test`, `TestStep`, `Measurement`, `ChannelGroup`, `Channel`. The filed 'query' holds the URL to load this layer of nodes. The first part of the query for each child object should be `/` plus the type of the layer in small letters followed by an `s`. For a nested child objected a filter is attached to the query in the form: `?filter=<parentType>.Id eq {<parentType>.Id}`. The placeholder <parentType> has to be replaced by the actual parent type (set in the field 'type' in the parent child or root layer, see example below). At runtime the curly braces will be replaced by the Id of the parent node. Further child objects, and thereby more sublayers, can be nested via the field 'children'. The children field does not need to be set for the last layer of nodes.
II.) Examples
a) Minimal node provider { "name": "My name to display", "type": "Environment"} b) node provider with two child layers { "name": "My name to display", "type": "Environment", "children": { "type": "Project", "query": "/projects", "children": { "type": "Pool", "query": "/pools?filter=Project.Id eq {Project.Id}" } } }
2.) Shopping basket file extensions
When downloading the contents of a shopping basket, a file with extension mdm
is generated. Additional file extensions can be adding by poviding a preference with key shoppingbasket.fileextensions
. Here you can define a list of objects with attributes ‘label’ and ‘extension’. For example: [ { "label": "MyTool", "extension": "mdm-mytool" }, { "label": "OtherTool", "extension": "mdm-other" } ]
. If MyTool
has a file handler registered for the extension mdm-mytool
, the application will be launched if the browser automatically opens the file after download.
Source scoped preferences are applied at any user but limited to the specified source. The source can be specified in the Add Preference
or Edit Preference
dialog.
1.) Ignored Attributes The ignore attributes preference must have the exact key ignoredAttributes
. An identifier must not be added. The preference specifies all attributes, which are supposed to be ignored in the detail view. The preference is a simple Json string holding a list of attributes in the form {“.”}. The placeholders and have to be replaced by the actual type and name of the attribute which should be ignored, respectively.
**Example: [“*.MimeType”, “TestStep.Sortindex”]
##Create a module Any MDM module needs to be a valid angular2 module. An angular2 module consists of one angular2 component and one ng-module at least. In the angular2 component any content can be defined. The component must be declared in a ng-module to grant accessibility in the rest of the application. In a module additional components and services can be defined. All related files should be stored in a new subfolder in the app folder
###Angular2 components (see example 1) A component is defined in a typescript file starting with the @Component() identifier. Any html content can be provided here in an inline template or via a link to an external html resource. Thereafter the component itself, which is supposed to hold any logic needed, is defined and exported.
###Ng-module (see example 2) On one hand the ng-module provides other MDM modules of the application, and thereby all the services and components declared within them, in this MDM module. The ‘imports’ array holds all modules from the application needed in this MDM module. It should always hold the MDMCoreModule, which provides basic functionalities. On the other hand a ng-module grants accessibility of components of this module in other directives (including the html template) within the module (in a ‘declaration’ array) or even in other parts of the application (in an ‘export’ array). For more details see *https://angular.io/docs/ts/latest/guide/ngmodule.html.
** Minimal example First create a new folder
import {Component} from ‘@angular/core’; @Component({template: ‘Example Module’}) export class MDMNewComponent {}
import { NgModule } from ‘@angular/core’; import { MDMCoreModule } from ‘../core/mdm-core.module’; import { MDMNewComponent } from ‘./mdm-new.component’; @NgModule({imports: [MDMCoreModule], declarations: [MDMNewComponent]}) export class MDMNewModule {}
###Embedding a module (no lazy loading) To embed this new module in MDM you have to register this module in the MDMModules Module. This is done by registering the component to moduleRoutes in modules-routing.module.ts:
{ path: ‘new’, component: MDMNewComponent}
Furthermore you have to define a display name for the registered route in the array returned by getLinks in modules.component.ts:
{ path: ‘new’, name: ‘New Module’ }
For further information refer to the Angular 2 documentation for modules & router:
###Lazy loading and routing module For lazy-loading (recommended in case there is a high number of modules) embedding of the module is slightly different.
{ path: ‘example’, loadChildren: ‘../example-module/mdm-example.module#MDMExampleModule’}
Additionally, a ng-module, the so called routing module, is needed to provide the routes to this modules components.
const moduleRoutes: Routes = [{ path: '', component: MDMExampleComponent }]; @NgModule({imports: [RouterModule.forChild(moduleRoutes)], exports: [RouterModule]}) export class MDMExampleRoutingModule {}
The routing module needs to be declared in the ng-module of this module as well. A full example is provided in
###Filerelease module The filerelease module is stored in the following folder: *..\org.eclipse.mdm.nucleus\org.eclipse.mdm.application\src\main\webapp\src\app\filerelease
It can be embedded as any other module described above. Register to moduleRoutes in modules-routing.module.ts:
{ path: ‘filerelease’, component: MDMFilereleaseComponent }
Add entry to links array in MDMModulesComponent:
{ name: ‘MDM Suche’, path: ‘search’}
To make the filerelease module available in the detail view it needs to be imported in the corresponding ng-module mdm-detail.module.ts. Thereafter, the MDMFilereleaseCreateComponent can be imported to the mdm-detail-view.component.ts. Then the following has to be added to the mdm-detail-view.component.html file:
<mdm-filerelease-create [node]=selectedNode [disabled]=“isReleasable()”>
It should be located right after the add to basket button:
Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
See the NOTICE file(s) distributed with this work for additional information regarding copyright ownership.
This program and the accompanying materials are made available under the terms of the Eclipse Public License v. 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.
SPDX-License-Identifier: EPL-2.0