Merge branch 'gui'
diff --git a/README.md b/README.md
index c0dd0cb..a8c305c 100644
--- a/README.md
+++ b/README.md
@@ -18,13 +18,14 @@
 
 ## build, deploy and configure the application
 
-1. **edit** the **org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/app** and set the variables host, port and prefix for your deployment
+1. **edit** the **org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/app/core/property.service.ts** and set the variables host, port and prefix for your deployment
 (This properties are used to create the rest URLs to communicate with the backend)
+Furthermore, specify the **contextPath** in **org.eclipse.mdm.nucleus/org.eclipse.mdm.application/src/main/webapp/webpack.config.js**
 2. **build** the application (gradlew install)
 The command **gradlew install** at **org.eclipse.mdm.nucleus** creates a ZIP archive named **mdm_web.zip** at
 **/org.eclipse.mdm.nucleus/build/distributions**
 The ZIP archive contains the backend **org.eclipse.mdm.nucleus.war** and the configurations **/configuration**
-3. **deploy** the backend ( **org.eclipse.mdm.nuclues.war** file) at your application server
+3. **deploy** the backend ( **org.eclipse.mdm.nuclues.war** file) at your application server, check that database for preference service is running (**asadmin start-database**)
 4. **copy the content** of the extracted **/configuration** folder to **GLASSFISH_ROOT/glassfish/domains/domain1/config**
 5. **edit** the **org.eclipse.mdm.connector/service.xml** file to configure the data sources
 6. **install** and **configure** the **LoginModule** (see org.eclipse.mdm.realms - README.md)
@@ -35,17 +36,17 @@
 
 
 ## available rest URLs
-   
-**Business Object: Environment** 
+
+**Business Object: Environment**
 
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/localizations
 * _example: http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments_
-      
+
 **Business Object: Test**
 
-* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/tests 
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/tests
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/tests?filter=FILTERSTRING
 * _example:  [http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMDATASOURCE1/tests?filter=Test.Name eq t*](http://localhost:8080/org.eclipse.mdm.nucleus/mdm/MDMDATASOURCE1/tests?filter=Test.Name%20eq%20t*)_
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/tests/searchattributes
@@ -80,23 +81,66 @@
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/measurements/TESTSTEPID/contexts/testsequence
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/measurements/TESTSTEPID/contexts/testequipment
 * _example: http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMDATASOURCE1/measurements/12345/contexts_
-   
+
 **Business Object: ChannelGroup**
 
-* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/channelgroups 
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/channelgroups
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/channelgroups/localizations
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/channelgroups/CHANNELGROUPID
-* _example: http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMDATASOURCE1/channelgroups/12345_ 
-   
+* _example: http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMDATASOURCE1/channelgroups/12345_
+
 **Business Object: Channel**
 
-* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/channels 
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/channels
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/channels/localizations
 * http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/channels/CHANNELID
 * _example: http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMDATASOURCE1/channels/123456_
 
-

-

+**Business Object: Project**
+
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/projects
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/projects?filter=FILTERSTRING
+* _example:  [http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMDATASOURCE1/projects?filter=Project.Name eq p*](http://localhost:8080/org.eclipse.mdm.nucleus/mdm/MDMDATASOURCE1/projects?filter=Project.Name%20eq%20p*)_
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/projects/searchattributes
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/projects/localizations
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/projects/PROJECTID
+* _example: http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMDATASOURCE1/projects/123_
+
+**Business Object: Pool**
+
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/pools
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/pools?filter=FILTERSTRING
+* _example:  [http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMDATASOURCE1/pools?filter=Pool.Name eq p*](http://localhost:8080/org.eclipse.mdm.nucleus/mdm/MDMDATASOURCE1/pools?filter=Pool.Name%20eq%20p*)_
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/pools/searchattributes
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/pools/localizations
+* http://SERVER:PORT/APPLICATIONROOT/mdm/environments/SOURCENAME/pools/POOLID
+* _example: http://localhost:8080/org.eclipse.mdm.nucleus/mdm/environments/MDMDATASOURCE1/pools/123_
+
+
+**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`
+
+
+
+## Preference Service
+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.
+
+### available rest URLs
+* http://SERVER:POART/APPLICATIONROOT/mdm/preferences
+* example: `curl -GET -H "Content-Type: application/json" http://localhost:8080/org.eclipse.mdm.nucleus/mdm/preferences?scope=SYSTEM&key=ignoredAttributes`
+* example: `curl -PUT -H "Content-Type: application/json" -d '{"scope": "SYSTEM", "key": "ignoredAttributes", "value": "[\"*.MimeType\"]"}' http://localhost:8080/org.eclipse.mdm.nucleus/mdm/preferences`
+* example: `curl -DELETE http://localhost:8080/org.eclipse.mdm.nucleus/mdm/preferences/ID`
+
+
 ## FreeTextSearch
 ### Configuration
 1. **start** ElasticSearch. ElasticSearch can be downloaded at https://www.elastic.co/products/elasticsearch. For testing purpose, it can be simply started by executing bin/run.bat
@@ -106,3 +150,204 @@
 ### Run on dedicated server
 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.
 
+
+##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
+
+## Client preferences
+
+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:
+  - Basket
+  - View
+  - Filter
+
+System:
+  - Node provider
+
+Source:
+  - Ignored attributes
+
+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.
+
+### User scope
+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 scope
+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
+  * ..\src\main\webapp\src\app\navigator\defaultnodeprovider.json.
+  It is recommended not to change the default node provider. Instead new node providers can be added as preferences. Their keys must start with the prefix ´nodeprovider.´. Once a custom node provider is supplied it can be selected in the dropdown menu in the navigation tree header.
+
+  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}"
+        }
+      }
+    }
+
+### Source scope
+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 {"<type>.<AttributeName>"}. The placeholders <type> and <AttributeName> 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
+* ..\org.eclipse.mdm.nucleus\org.eclipse.mdm.application\src\main\webapp\src\app.
+
+###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
+ * ..\org.eclipse.mdm.nucleus\org.eclipse.mdm.application\src\main\webapp\src\app\new-module
+
+ 1) Minimal angular2 component
+ Add a new typescript file named 'mdm-new.component.ts' with the following content:
+
+ import {Component} from '@angular/core';
+ @Component({template: '<h1>Example Module</h1>'})
+ export class MDMNewComponent {}
+
+ 2) Minimal ng-module
+ Add a new typescript file named 'mdm-new.module.ts' with the following content:
+
+ 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:
+* https://angular.io/docs/ts/latest/guide/ngmodule.html
+* https://angular.io/docs/ts/latest/guide/router.html
+
+
+ ###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
+  * ..\org.eclipse.mdm.nucleus\org.eclipse.mdm.application\src\main\webapp\src\app\example-module
+
+
+ ###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()"></mdm-filerelease-create>
+***
+
+
+
+It should be located right after the add to basket button:
+***
+<div class="btn-group pull-right" role="group">
+  <button type="button" class="btn btn-default" (click)="add2Basket()" [disabled]="isShopable()">In den Warenkorb</button>
+  <mdm-filerelease-create [node]=selectedNode [disabled]="isReleasable()"></mdm-filerelease-create>
+</div>
+***
diff --git a/build.gradle b/build.gradle
index f6521c0..f4e56da 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,6 +13,8 @@
 apply plugin: 'war'
 apply plugin: 'maven'
 apply plugin: 'eclipse'
+apply plugin: 'jacoco'
+apply plugin: 'org.sonarqube'
 
 sourceCompatibility = 1.8
 
@@ -28,6 +30,8 @@
 	apply plugin: 'eclipse'
 	apply plugin: 'java'
 	apply plugin: 'maven'
+	apply plugin: 'jacoco'
+	
 	sourceCompatibility = 1.8
     
     repositories {
@@ -63,6 +67,13 @@
 	}
 }
 
+task copySchema(dependsOn: ':org.eclipse.mdm.preferences:generateSchema') {
+  copy {
+    from "${project.projectDir}/org.eclipse.mdm.preferences/build/generated-schema/"
+    into "${project.projectDir}/build/tmp/mdmweb/schema/org.eclipse.mdm.preferences"
+  }
+}
+
 task distribute(type: Zip) {
 	archiveName = "mdm_web.zip"
 	from "${project.projectDir}/build/tmp/mdmweb"
@@ -80,5 +91,33 @@
 } 
 
 war.finalizedBy(collectConfiguration)
+war.finalizedBy(copySchema)
 war.finalizedBy(distribute)
 
+jacoco {
+	toolVersion = "0.7.6.201602180812"
+    reportsDir = file("${project.buildDir}/jacoco/test.exec")
+}
+
+sonarqube {
+    properties {
+        property "sonar.java.coveragePlugin", "jacoco"
+        property "sonar.jacoco.reportPaths", "${project.buildDir}/jacoco/test.exec"
+    }
+}
+tasks["sonarqube"].dependsOn "org.eclipse.mdm.application:map_tslint"
+
+buildscript {
+    repositories {
+      maven {
+        url "http://repo1.maven.org/maven2/"
+      }
+      maven {
+        url "https://plugins.gradle.org/m2/"
+      }
+      mavenLocal()
+    }
+    dependencies {
+      classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.2.1'
+    }
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.application/build.gradle b/org.eclipse.mdm.application/build.gradle
index f2f2d8c..ef0eb1f 100644
--- a/org.eclipse.mdm.application/build.gradle
+++ b/org.eclipse.mdm.application/build.gradle
@@ -16,6 +16,32 @@
 
 System.setProperty('file.encoding', 'UTF-8')
 
+
+task map_tslint(type: Copy) {
+	File tsLintFile = file("${project.buildDir}/node/coverage/tslint_out.json")
+	File mappedTsLintFile = file("${project.buildDir}/node/coverage/tslint_out_mapped.json")
+	
+	if (tsLintFile.exists()) {
+		String contents = tsLintFile.getText('UTF-8') 
+		contents = contents.replaceAll( 'src/app', 'src/main/webapp/src/app' )
+		mappedTsLintFile.write(contents, 'UTF-8')
+	}
+}
+
+sonarqube {
+    properties {
+        property "sonar.sources", "src/main/webapp/src,src/main/java"
+        property "sonar.exclusions", "src/main/webapp/node_modules/**, src/main/webapp/coverage/**, src/main/webapp/dist/**,**/*.spec.ts, **/*.js"
+        property "sonar.tests", "src/main/webapp/src"
+		    property "sonar.test.inclusions", "**/*.spec.ts"
+		
+		    property "sonar.ts.tslint.configPath", "src/main/webapp"
+		    property "sonar.ts.tslint.projectPath", "src/main/webapp"
+        property "sonar.ts.tslint.outputPath", "${project.buildDir}/node/coverage/tslint_out_mapped.json"
+        property "sonar.ts.coverage.lcovReportpath", "${project.buildDir}/node/coverage/lcov.info"
+    }
+}
+
 dependencies {
 
 	runtime 'org.glassfish.jersey.containers:jersey-container-servlet:2.15'
@@ -25,6 +51,7 @@
 	compile project(':org.eclipse.mdm.filerelease')
 	compile project(':org.eclipse.mdm.property')	
 	compile project(':org.eclipse.mdm.freetextindexer')	
+	compile project(':org.eclipse.mdm.preferences')	
 }
 
 buildscript {
@@ -34,46 +61,54 @@
 		} 
 	}
 	dependencies { 
-		classpath "com.moowork.gradle:gradle-node-plugin:0.12" 
+		classpath "com.moowork.gradle:gradle-node-plugin:1.0.1" 
 	}
 }
 
 
 task copy_websources(type: Copy) {
 
-	from './src/main/webapp'	
+	from('./src/main/webapp') {
+		exclude 'dist'
+		exclude 'node_modules'
+	}
+	
 	into "${project.buildDir}/node"
 }
 
 
 node {
-	version = '4.3.2'
-	npmVersion = '3.9.2'
+	version = '6.9.4'
+	npmVersion = '3.10.10'
 	distBaseUrl = 'https://nodejs.org/dist'
 	download = true
 	workDir = file("${project.buildDir}/node/nodejs")
+	npmWorkDir = file("${project.buildDir}/node")
 	nodeModulesDir = file("${project.buildDir}/node")
 	if (!nodeModulesDir.isDirectory()) {
 		nodeModulesDir.mkdirs()
 	}
 }
+npm_install {
+	args = ["${project.buildDir}"]
+}
 
-task webpack(type: NodeTask) {
-  	script = file("${project.buildDir}/node/node_modules/webpack/bin/webpack.js")
-  	 args = ['--config', "${project.buildDir}/node/webpack.config.js"]
+task runBuild(type: NpmTask) {
+    args = ['run', 'ci_build']
 }
 
 task copy_loginsites(type: Copy) {
-	from 	"${project.buildDir}/node/login.jsp", 
-			"${project.buildDir}/node/login.css", 
-			"${project.buildDir}/node/error.jsp"
+	from 	"${project.buildDir}/node/src/login.jsp", 
+			"${project.buildDir}/node/src/login.css", 
+			"${project.buildDir}/node/src/error.jsp"
 			
 	into	 "${project.buildDir}/node/dist"
 }
 
 compileJava.dependsOn(copy_loginsites)
-copy_loginsites.dependsOn(webpack)
-webpack.dependsOn(npmInstall)
+map_tslint.dependsOn(runBuild)
+copy_loginsites.dependsOn(runBuild)
+runBuild.dependsOn(npmInstall)
 npmInstall.dependsOn(copy_websources)
 copy_websources.dependsOn(npmSetup)
 npmSetup.dependsOn(nodeSetup)
diff --git a/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/logout/MDMRequestFilter.java b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/logout/MDMRequestFilter.java
index fdf292e..421187f 100644
--- a/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/logout/MDMRequestFilter.java
+++ b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/logout/MDMRequestFilter.java
@@ -31,7 +31,7 @@
  */
 public class MDMRequestFilter implements Filter {
 
-	private static String SERVLET_NAME_MDMNENUE = "mdmmenu";
+	private static String SERVLET_NAME_MDMNENUE = "/navigator/";
 	
 	@Inject
 	private MDMSessionExpiredListener sessionExpiredListener;
@@ -53,7 +53,7 @@
 			HttpServletRequest httpRequest = (HttpServletRequest)request;
 			String requestedURL = httpRequest.getRequestURI().toLowerCase();
 			
-			if(requestedURL.trim().endsWith(SERVLET_NAME_MDMNENUE)) {
+			if(requestedURL.trim().contains(SERVLET_NAME_MDMNENUE)) {
 				if(response instanceof HttpServletResponse) {
 					String location = httpRequest.getContextPath();
 					((HttpServletResponse) response).sendRedirect(location);
diff --git a/org.eclipse.mdm.application/src/main/webapp/.editorconfig b/org.eclipse.mdm.application/src/main/webapp/.editorconfig
new file mode 100644
index 0000000..6e87a00
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/.editorconfig
@@ -0,0 +1,13 @@
+# Editor configuration, see http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
diff --git a/org.eclipse.mdm.application/src/main/webapp/.gitignore b/org.eclipse.mdm.application/src/main/webapp/.gitignore
new file mode 100644
index 0000000..0742f7a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/.gitignore
@@ -0,0 +1,41 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/dist
+/tmp
+/coverage
+
+# dependencies
+/node_modules
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage/*
+/libpeerconnection.log
+npm-debug.log
+testem.log
+/typings
+
+# e2e
+/e2e/*.js
+/e2e/*.map
+
+#System Files
+.DS_Store
+Thumbs.db
diff --git a/org.eclipse.mdm.application/src/main/webapp/README.md b/org.eclipse.mdm.application/src/main/webapp/README.md
new file mode 100644
index 0000000..24ec52f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/README.md
@@ -0,0 +1,31 @@
+# Webapp
+
+This project was generated with [angular-cli](https://github.com/angular/angular-cli) version 1.0.0-beta.28.3.
+
+## Development server
+Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+
+## Code scaffolding
+
+Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
+
+## Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
+
+## Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Running end-to-end tests
+
+Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+Before running the tests make sure you are serving the app via `ng serve`.
+
+## Deploying to GitHub Pages
+
+Run `ng github-pages:deploy` to deploy to GitHub Pages.
+
+## Further help
+
+To get more help on the `angular-cli` use `ng help` or go check out the [Angular-CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
diff --git a/org.eclipse.mdm.application/src/main/webapp/angular-cli.json b/org.eclipse.mdm.application/src/main/webapp/angular-cli.json
new file mode 100644
index 0000000..42901ec
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/angular-cli.json
@@ -0,0 +1,57 @@
+{
+  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+  "project": {
+    "version": "1.0.0-beta.32.3",
+    "name": "openMDM-Web"
+  },
+  "apps": [
+    {
+      "root": "src",
+      "outDir": "dist",
+      "assets": [
+        "assets",
+        "favicon.ico"
+      ],
+      "index": "index.html",
+      "main": "main.ts",
+      "polyfills": "polyfills.ts",
+      "test": "test.ts",
+      "tsconfig": "tsconfig.json",
+      "prefix": "app",
+      "styles": [
+        "../node_modules/primeng/resources/themes/bootstrap/theme.css",
+        "../node_modules/primeng/resources/primeng.min.css",
+        "../node_modules/font-awesome/css/font-awesome.min.css",
+        "../node_modules/bootstrap/dist/css/bootstrap.min.css",
+        "styles.css"
+      ],
+      "scripts": [],
+      "environmentSource": "environments/environment.ts",
+      "environments": {
+        "dev": "environments/environment.ts",
+        "prod": "environments/environment.prod.ts"
+      }
+    }
+  ],
+  "e2e": {
+    "protractor": {
+      "config": "./protractor.conf.js"
+    }
+  },
+  "lint": [
+    {
+      "files": "src/**/*.ts",
+      "project": "src/tsconfig.json"
+    }
+  ],
+  "test": {
+    "karma": {
+      "config": "./karma.conf.js"
+    }
+  },
+  "defaults": {
+    "styleExt": "css",
+    "component": {},
+    "inline": true
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/basket/basket.service.ts b/org.eclipse.mdm.application/src/main/webapp/app/basket/basket.service.ts
deleted file mode 100644
index b093fa1..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/basket/basket.service.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Injectable} from '@angular/core';
-
-import {Node} from '../navigator/node';
-
-@Injectable()
-export class BasketService {
-  Nodes: Node[] = [];
-
-  addNode(node){
-    var index = this.Nodes.indexOf(node);
-    if (index == -1) {
-      this.Nodes.push(node)
-    }
-  }
-  removeNode(node){
-    var index = this.Nodes.indexOf(node);
-    if (index > -1) {
-      this.Nodes.splice(index, 1);
-    }
-  }
-  removeAll(){
-    this.Nodes = [];
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/basket/mdm-basket.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/basket/mdm-basket.component.ts
deleted file mode 100644
index 83ea327..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/basket/mdm-basket.component.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, Input, Output, EventEmitter} from '@angular/core';
-import {Node} from '../navigator/node';
-import {BasketService} from './basket.service';
-
-@Component({
-  selector: 'mdm-basket',
-  template: require('../../templates/basket/mdm-basket.component.html'),
-  styles: ['.remove {color:black; cursor: pointer; float: right}'],
-  directives: [],
-  providers: []
-})
-export class MDMBasketComponent {
-  @Output() onSelect = new EventEmitter<Node>();
-  @Output() onActive = new EventEmitter<Node>();
-  @Input() activeNode: Node;
-
-  constructor(private _basketService : BasketService){}
-
-  isActive(node){
-    if (this.activeNode == node) {
-      return "active"
-    }
-  }
-
-  removeNode(node){
-    this._basketService.removeNode(node);
-  }
-
-  selectNode(node){
-    this.activeNode = node;
-    this.onActive.emit(node);
-    this.onSelect.emit(node);
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/details/context.service.ts b/org.eclipse.mdm.application/src/main/webapp/app/details/context.service.ts
deleted file mode 100644
index fef3482..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/details/context.service.ts
+++ /dev/null
@@ -1,161 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Injectable} from '@angular/core';
-import {Http, Response, Headers, RequestOptions} from '@angular/http';
-import {Observable} from 'rxjs/Observable';
-import {Context, Sensor} from './context';
-import {PropertyService} from '../properties';
-
-import {Node} from '../navigator/node';
-
-import {Components} from './context';
-
-@Injectable()
-export class ContextService {
-  constructor(private http: Http,
-              private _prop: PropertyService){
-                if (this._prop.api_host){this._contextUrl = this._prop.api_host + this._contextUrl}
-              }
-
-  private _contextUrl = 'mdm/environments'
-
-  private test : {}
-  private errorMessage: string;
-
-  getContext(node: Node){
-    let url = this._contextUrl + "/" + node.sourceName;
-    url = url + "/" + node.type.toLowerCase() + "s/" + node.id + '/contexts';
-    return this.get(url)
-  }
-  getSensors(node: Node){
-    let url = this._contextUrl + "/" + node.sourceName + "/" + node.type.toLowerCase() + "s/" + node.id + "/contexts/testequipment/sensors"
-    return this.http.get(url)
-        .map((res) => {return <{}> this.merge(res.json().data)})
-        .catch(this.handleError);
-  }
-
-  private get(url: string){
-    return this.http.get(url)
-    .map((res) => {
-      let data = res.json().data
-      let context = this.specialMerger([data[0].contextOrdered, data[0].contextMeasured]);
-      return <{}> context
-    })
-    .catch(this.handleError);
-  }
-
-  private merge(sensor: Sensor) {
-    let sensorm = sensor[0].sensorContextMeasured
-    let sensoro = sensor[0].sensorContextOrdered
-    let merge = []
-    sensoro.forEach((node) => {
-      let pos = sensorm.map(function(e) { return e.name; }).indexOf(node.name);
-      if (pos == -1) {
-        merge.push(this.empty_m(node))
-      } else {
-        merge.push(this.mergeNode(node, sensorm[pos]))
-        sensorm.splice(pos, 1);
-      }
-    });
-    sensorm.forEach((node) => {
-      merge.push(this.empty_o(node))
-    })
-    return merge
-  }
-
-  private mergeNode(oNode, mNode){
-    oNode.attributes.forEach((attr, i) => {
-      attr.dataType = [attr.dataType, mNode.attributes[i].dataType]
-      attr.name = [attr.name, mNode.attributes[i].name]
-      attr.unit = [attr.unit, mNode.attributes[i].unit]
-      attr.value = [attr.value, mNode.attributes[i].value]
-    })
-    return oNode
-  }
-
-  private empty_o(node){
-    node.attributes.forEach((attr) => {
-      attr.dataType = ["", attr.dataType]
-      attr.name = ["", attr.name]
-      attr.unit = ["", attr.unit]
-      attr.value = ["", attr.unit]
-    })
-    return node
-  }
-
-  private empty_m(node){
-    node.attributes.forEach((attr) => {
-      attr.dataType = [attr.dataType, ""]
-      attr.name = [attr.name, ""]
-      attr.unit = [attr.unit, ""]
-      attr.value = [attr.unit, ""]
-    })
-    return node
-  }
-
-  private specialMerger(contexts) {
-		var result = new Object();
-		var resultattributegroup, resultattributes, resultattribute;
-
-		for (var i = 0; i < contexts.length; ++i) {
-			for (var testname in contexts[i]) {
-				var test = contexts[i][testname];
-				if (!Array.isArray(test)) continue;
-
-				if (!result[testname]) result[testname] = new Array();
-				var subresult = result[testname];
-
-				for (var j = 0; j < test.length; ++j) {
-					var attributegroup = test[j];
-					if (!(attributegroup instanceof Object)) continue;
-
-					var index = -1;
-					subresult.forEach(function (resultattributegroup, idx) {
-						if (resultattributegroup["name"] == attributegroup["name"]) { index = idx; return true; }
-					});
-					if (index < 0) {
-						index = subresult.length;
-						subresult.push(resultattributegroup = JSON.parse(JSON.stringify(attributegroup)));
-						resultattributegroup["attributes"] = new Array();
-					}
-					else resultattributegroup = subresult[index];
-					resultattributes = resultattributegroup["attributes"];
-
-					var attributes = attributegroup["attributes"];
-					if (!Array.isArray(attributes)) continue;
-
-					for (var k = 0; k < attributes.length; ++k) {
-						var attribute = attributes[k];
-						if (!(attribute instanceof Object)) continue;
-
-						var index = -1;
-						resultattributes.forEach(function (resultattribute, idx) {
-							if (resultattribute["name"] == attribute["name"]) { index = idx; return true; }
-						});
-						if (index < 0) {
-							index = resultattributes.length;
-							resultattributes.push(resultattribute = JSON.parse(JSON.stringify(attribute)));
-							resultattribute["value"] = new Array(contexts.length);
-						}
-						else resultattribute = resultattributes[index];
-						resultattribute["value"][i] = attribute["value"];
-					}
-				}
-			}
-		}
-		return result;
-	}
-
-  private handleError(error: Response) {
-    console.error(error);
-    return Observable.throw(error.json().error || 'Server error');
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/details/context.ts b/org.eclipse.mdm.application/src/main/webapp/app/details/context.ts
deleted file mode 100644
index 34ca83e..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/details/context.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Node} from '../navigator/node';
-
-export class Context {
-  contextMeasured: Components;
-  contextOrdered: Components;
-}
-
-export class Components {
-  UNITUNDERTEST: Node[];
-  TESTSEQUENCE: Node[];
-  TESTEQUIPMENT: Node[];
-}
-
-export class Sensor {
-  sensorContextMeasured: Node[];
-  sensorContextOrdered: Node[];
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/details/mdm-detail-descriptive-data.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/details/mdm-detail-descriptive-data.component.ts
deleted file mode 100644
index 1e607ec..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/details/mdm-detail-descriptive-data.component.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, Input, OnChanges, SimpleChange} from '@angular/core';
-import {TAB_DIRECTIVES, ACCORDION_DIRECTIVES} from 'ng2-bootstrap/ng2-bootstrap';
-
-import {NodeService} from '../navigator/node.service';
-import {ContextService} from './context.service';
-import {Context, Sensor} from './context';
-import {Node} from '../navigator/node';
-
-@Component({
-  selector: 'mdm-detail-context',
-  template: require('../../templates/details/mdm-detail-descriptive-data.component.html'),
-  providers: [ContextService],
-  directives: [TAB_DIRECTIVES, ACCORDION_DIRECTIVES]
-})
-
-export class MDMDescriptiveDataComponent implements OnChanges{
-  @Input() selectedNode: Node;
-
-  constructor(private _nodeService: NodeService,
-              private _contextService: ContextService){}
-
-  _diff: boolean = false;
-  contexts: Context[];
-  sensors: Sensor[];
-  errorMessage: string;
-  status: string = "loading...";
-
-  uut:string = "Prüfling";
-  ts:string = "Messgerät";
-  te:string = "Testablauf";
-  s:string = "Sensoren";
-
-  ngOnChanges(changes: {[propName: string]: SimpleChange}){
-    this.getContext(changes['selectedNode'].currentValue);
-  }
-
-  getContext(node: Node){
-    this.contexts = undefined;
-    if (node.name != undefined && (node.type.toLowerCase() == "measurement" || node.type.toLowerCase() == "teststep")) {
-      this.status = "loading...";
-      this._contextService.getContext(node).subscribe(
-        contexts => this.contexts = contexts,
-        error => this.errorMessage = <any>error);
-      this._contextService.getSensors(node).subscribe(
-          sensors => this.sensors = sensors,
-          error => this.errorMessage = <any>error);
-    } else {
-      this.status = "keine Beschreibende Daten verfügbar"
-    }
-  }
-
-
-  diffToggle(){
-    this._diff = !this._diff
-  }
-  diff(attr1: string, attr2: string){
-    if (attr1 !== attr2 && this._diff) {
-      return 'danger';
-    }
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/details/mdm-detail-view.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/details/mdm-detail-view.component.ts
deleted file mode 100644
index fda7faf..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/details/mdm-detail-view.component.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, Input} from '@angular/core';
-import {ControlGroup, Control, Validators} from '@angular/common';
-
-import {Node} from '../navigator/node';
-import {NodeService} from '../navigator/node.service';
-import {BasketService} from '../basket/basket.service';
-
-import {Release, FilereleaseService} from '../filerelease/filerelease.service';
-
-import {Localization} from '../localization/localization';
-import {LocalizationService} from '../localization/localization.service';
-
-import {MODAL_DIRECTVES, BS_VIEW_PROVIDERS} from 'ng2-bootstrap/ng2-bootstrap';
-import {MDMFilereleaseCreateComponent} from '../filerelease/mdm-filerelease-create.component';
-
-@Component({
-  selector: 'mdm-detail-view',
-  template: require('../../templates/details/mdm-detail-view.component.html'),
-  directives: [MODAL_DIRECTVES, MDMFilereleaseCreateComponent],
-  providers: [],
-  viewProviders: [BS_VIEW_PROVIDERS],
-  inputs: []
-})
-
-export class MDMDetailViewComponent{
-  @Input() selectedNode: Node;
-  locals: Localization[] = [];
-
-  constructor(private _nodeService: NodeService,
-              private _loaclService: LocalizationService,
-              private _basketService: BasketService,
-              private _releaseService: FilereleaseService){}
-
-  add2Basket(){
-    if (this.selectedNode){
-      this._basketService.addNode(this.selectedNode);
-    }
-  }
-
-  isShopable(){
-    if (this.selectedNode.name != undefined){return false}
-    return true
-  }
-  isReleasable(){
-    if (this.selectedNode.sourceType == 'TestStep'){return false}
-    return true
-  }
-
-  getTrans(type: string, attr: string){
-    return this._loaclService.getTranslation(type, attr)
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/details/mdm-detail.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/details/mdm-detail.component.ts
deleted file mode 100644
index a815531..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/details/mdm-detail.component.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, Input} from '@angular/core';
-import {HTTP_PROVIDERS} from '@angular/http';
-import {RouteConfig, ROUTER_DIRECTIVES} from '@angular/router-deprecated';
-
-import {Node} from '../navigator/node';
-
-import {MDMDetailViewComponent} from './mdm-detail-view.component';
-import {MDMDescriptiveDataComponent} from './mdm-detail-descriptive-data.component';
-import {MDMSearchComponent} from '../search/mdm-search.component';
-import {MDMFullTextSearchComponent} from '../search/mdm-full-text-search.component';
-import {MDMFilereleaseComponent} from '../filerelease/mdm-filerelease.component';
-
-@Component({
-  selector: 'mdm-detail',
-  template: require('../../templates/details/mdm-detail.component.html'),
-  directives: [ROUTER_DIRECTIVES, MDMDetailViewComponent, MDMDescriptiveDataComponent, MDMSearchComponent, MDMFullTextSearchComponent, MDMFilereleaseComponent],
-  providers: []
-})
-export class MDMDetailComponent {
-  @Input() selectedNode: Node;
-
-  brand: string = 'Details';
-  _comp: string = 'DetailView';
-
-  activate(comp: string){
-    this._comp = comp;
-  }
-  isActive(comp: string){
-    if (comp === this._comp) {
-      return 'active';
-    }
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/filerelease/filerelease.service.ts b/org.eclipse.mdm.application/src/main/webapp/app/filerelease/filerelease.service.ts
deleted file mode 100644
index 7e908f9..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/filerelease/filerelease.service.ts
+++ /dev/null
@@ -1,132 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Injectable} from '@angular/core';
-import {Http, Response, Headers, RequestOptions} from '@angular/http';
-import {Observable} from 'rxjs/Observable';
-
-import {PropertyService} from '../properties';
-
-@Injectable()
-export class FilereleaseService {
-  private _fileUrl = "mdm/filereleases"
-  stateMap = new Array();
-  formatMap = new Array();
-  month = new Array();
-
-  constructor(private http: Http,
-              private _prop: PropertyService){
-                  if (this._prop.api_host){this._fileUrl = this._prop.api_host + this._fileUrl}
-                  this.formatMap["PAK2RAW"] = "original Daten"
-                  this.formatMap["PAK2ATFX"] = "ATFX"
-                  this.stateMap["RELEASE_ORDERED"] = "beauftragt"
-                  this.stateMap["RELEASE_APPROVED"] = "genehmigt"
-                  this.stateMap["RELEASE_RELEASED"] = "freigegeben"
-                  this.stateMap["RELEASE_EXPIRED"] = "abgelaufen"
-                  this.stateMap["RELEASE_PROGRESSING_ERROR"] = "Systemfehler"
-                  this.stateMap["RELEASE_PROGRESSING"] = "In Bearbeitung"
-                  this.stateMap["RELEASE_REJECTED"] = "abgelehnt"
-                  this.month[0]="1"
-                  this.month[1]="2"
-                  this.month[2]="3"
-                  this.month[3]="4"
-                  this.month[4]="5"
-                  this.month[5]="6"
-                  this.month[6]="7"
-                  this.month[7]="8"
-                  this.month[8]="9"
-                  this.month[9]="10"
-                  this.month[10]="11"
-                  this.month[11]="12"
-              }
-
-  readAll(){
-    return this.read("")
-  }
-  readIncomming(){
-    return this.read("?direction=incomming")
-  }
-  readOutgoging(){
-    return this.read("?direction=outgoing")
-  }
-  private read(query: string){
-    return this.http.get(this._fileUrl + query)
-    .map(res => <Release[]> res.json().data)
-    .catch(this.handleError);
-  }
-
-  create(release: Release){
-    let body = JSON.stringify(release);
-    let headers = new Headers({ 'Content-Type': 'application/json' });
-    let options = new RequestOptions({ headers: headers });
-    return this.http.post(this._fileUrl, body, options)
-                    .map(this.extractData)
-                    .catch(this.handleError);
-  }
-  delete(release: Release){
-    return this.http.delete(this._fileUrl + "/" + release.identifier)
-  }
-
-  approve(release: Release){
-    release.state = "RELEASE_APPROVED"
-    return this.update(release)
-  }
-  reject(release: Release){
-    release.state = "RELEASE_REJECTED"
-    return this.update(release)
-  }
-  private update(release: Release){
-    let body = JSON.stringify(release);
-    let headers = new Headers({ 'Content-Type': 'application/json' });
-    let options = new RequestOptions({ headers: headers });
-    return this.http.post(this._fileUrl + "/" + release.identifier, body, options)
-                    .map(this.extractData)
-                    .catch(this.handleError);
-  }
-
-  private handleError(error: Response) {
-    console.error(error);
-    return Observable.throw(error.json().error || 'Server error');
-  }
-  private extractData(res: Response) {
-    let body = res.json();
-    return body.data || { };
-  }
-
-  formatDate(date){
-    var d = new Date(date);
-    var day = d.getDate()
-    var month = this.month[d.getMonth()]
-    var year = d.getFullYear()
-    var hours = (d.getHours()<10?'0':'') + d.getHours()
-    var min = (d.getMinutes()<10?'0':'') + d.getMinutes()
-    var sec = (d.getSeconds()<10?'0':'') + d.getSeconds()
-    return day + "." + month + "." + year + " " + hours + ":" + min + ":" + sec
-  }
-
-}
-
-export class Release {
-  identifier: string;
-  state: string;
-  name: string;
-  sourceName: string;
-  typeName: string;
-  id: number;
-  sender: string;
-  receiver: string;
-  orderMessage: string;
-  rejectMessage: string;
-  errorMessage: string;
-  format: string;
-  fileLink: string;
-  validity:number;
-  expire: number;
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/filerelease/mdm-filerelease-create.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/filerelease/mdm-filerelease-create.component.ts
deleted file mode 100644
index 4b83d5b..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/filerelease/mdm-filerelease-create.component.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, Input, Output, EventEmitter} from '@angular/core';
-import {ControlGroup} from '@angular/common';
-import {Release, FilereleaseService} from './filerelease.service';
-import {Node} from '../navigator/node';
-
-@Component({
-  selector: 'mdm-filerelease-create',
-  template: require('../../templates/filerelease/mdm-filerelease-create.component.html'),
-  styles: [],
-  directives: [],
-  providers: []
-})
-export class MDMFilereleaseCreateComponent {
-
-  constructor(private service : FilereleaseService){}
-
-  @Input() node: Node;
-  @Output() onSubmit = new EventEmitter<boolean>();
-  release : Release = new Release;
-  errorMessage: string;
-  options = ["PAK2RAW","PAK2ATFX"]
-  expire = [1,2,3,4,5,6,7,8,9,10]
-
-  getFormat(key){
-      return this.service.formatMap[key]
-  }
-
-  createRelease(){
-    this.release.identifier = ""
-    this.release.state = ""
-    this.release.name = this.node.name
-    this.release.sourceName = this.node.sourceName
-    this.release.typeName = this.node.type
-    this.release.id = this.node.id
-    this.release.sender = ""
-    this.release.receiver = ""
-    this.release.rejectMessage = ""
-    this.release.errorMessage = ""
-    this.release.fileLink = ""
-    this.release.expire = 0
-    this.service.create(this.release).subscribe(
-      release => this.release = release,
-      error => this.errorMessage = <any>error);
-    this.clear()
-    this.onSubmit.emit(true)
-  }
-  clear(){
-    this.release = new Release()
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/filerelease/mdm-filerelease-display.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/filerelease/mdm-filerelease-display.component.ts
deleted file mode 100644
index ba0d126..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/filerelease/mdm-filerelease-display.component.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, Input} from '@angular/core';
-import {Release, FilereleaseService} from './filerelease.service';
-
-@Component({
-  selector: 'mdm-filerelease-display',
-  template: require('../../templates/filerelease/mdm-filerelease-display.component.html'),
-  styles: [],
-  directives: [],
-  providers: []
-})
-export class MDMFilereleaseDisplayComponent {
-  @Input() release: Release
-
-  constructor(private service: FilereleaseService){}
-
-  getFormat(format){
-    return this.service.formatMap[format]
-  }
-  getState(state){
-    return this.service.stateMap[state]
-  }
-  getDate(date){
-    if (date == 0) {return}
-    return this.service.formatDate(date)
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/filerelease/mdm-filerelease.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/filerelease/mdm-filerelease.component.ts
deleted file mode 100644
index 8f9ee1a..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/filerelease/mdm-filerelease.component.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, OnInit} from '@angular/core';
-import {FilereleaseService, Release} from './filerelease.service';
-import {MDMFilereleaseDisplayComponent} from './mdm-filerelease-display.component';
-import {PropertyService} from '../properties';
-
-import {MODAL_DIRECTVES, BS_VIEW_PROVIDERS} from 'ng2-bootstrap/ng2-bootstrap';
-
-@Component({
-  selector: 'mdm-filerelease',
-  template: require('../../templates/filerelease/mdm-filerelease.component.html'),
-  styles: ['.box1 {border: 1px solid #ddd; border-radius: 4px;}',
-           '.box2 {border: 1px solid #ddd; border-radius: 4px; margin-top: 20px}'],
-  directives: [MODAL_DIRECTVES, MDMFilereleaseDisplayComponent],
-  viewProviders: [BS_VIEW_PROVIDERS],
-  providers: []
-})
-export class MDMFilereleaseComponent implements OnInit{
-  constructor(private service: FilereleaseService,
-              private prop: PropertyService){}
-
-  incoming: Release[] =[]
-  outgoing: Release[] =[]
-  errorMessage: string
-  release: Release = new Release
-  event: string = "display"
-  dataHost: string = this.prop.data_host
-
-  ngOnInit(){
-    this.getReleases()
-  }
-
-  getReleases(){
-    this.service.readOutgoging().subscribe(
-      releases => this.outgoing = releases,
-      error => this.errorMessage = <any>error);
-    this.service.readIncomming().subscribe(
-      releases => this.incoming = releases,
-      error => this.errorMessage = <any>error);
-  }
-  setData(release){
-    this.release = release
-  }
-  getState(release: Release){
-    if (release.state == "RELEASE_ORDERED") {return "info"}
-    if (release.state == "RELEASE_APPROVED") {return "warning"}
-    if (release.state == "RELEASE_RELEASED") {return "success"}
-    if (release.state == "RELEASE_EXPIRED") {return "danger"}
-    if (release.state == "RELEASE_REJECTED") {return "danger"}
-    if (release.state == "RELEASE_PROGRESSING_ERROR") {return "danger"}
-    if (release.state == "RELEASE_PROGRESSING") {return "warning"}
-    return "info"
-  }
-  rejectRelease(reason: string){
-    this.release.rejectMessage = reason
-    this.service.reject(this.release).subscribe(
-      release => this.updateList(release),
-      error => this.errorMessage = <any>error);
-  }
-  approveRelease(){
-    this.service.approve(this.release).subscribe(
-      release => this.updateList(release),
-      error => this.errorMessage = <any>error);
-    this.release.state = "RELEASE_PROGRESSING"
-  }
-  updateList(id){
-    let pos = this.outgoing.map(function(e) { return e.identifier; }).indexOf(id)
-    if (pos != -1) {this.outgoing.splice(pos, 1);}
-    pos = this.incoming.map(function(e) { return e.identifier; }).indexOf(id)
-    if (pos != -1) {this.incoming.splice(pos, 1);}
-  }
-  isDeletable(){
-    if (this.release.state != "RELEASE_PROGRESSING") {return false}
-    return true
-  }
-  deleteRelease(){
-    let id = this.release.identifier
-    this.service.delete(this.release).subscribe(data => this.updateList(id), err => console.log(err))
-  }
-  setEvent(event){
-    this.event = event
-  }
-  isReleaseable(){
-    if (this.release.state != "RELEASE_ORDERED") {return true}
-    return false
-  }
-  getFormat(format){
-    return this.service.formatMap[format]
-  }
-  getTransState(state){
-    return this.service.stateMap[state]
-  }
-  getDate(date){
-    if (date == 0) {return}
-    return this.service.formatDate(date)
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/localization/localization.service.ts b/org.eclipse.mdm.application/src/main/webapp/app/localization/localization.service.ts
deleted file mode 100644
index cb656d0..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/localization/localization.service.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Injectable} from '@angular/core';
-import {Http, Response, Headers, RequestOptions} from '@angular/http';
-import {Observable} from 'rxjs/Observable';
-import {Localization} from './localization';
-import {Node} from '../navigator/node';
-import {NodeService} from '../navigator/node.service';
-import {PropertyService} from '../properties';
-
-@Injectable()
-export class LocalizationService{
-  constructor(private http: Http,
-              private _prop: PropertyService,
-              private _node: NodeService){
-                if (this._prop.api_host){this._nodeUrl = this._prop.api_host + this._nodeUrl}
-                this.init()
-              }
-
-  private _nodeUrl = 'mdm/environments'
-
-  private _cache : Localization[] = [];
-  private errorMessage: string;
-
-  private init(){
-    let node: Node;
-    this._node.getNodes(node).subscribe(
-      envs => this.initLocalization(envs),
-      error => this.errorMessage = <any>error);
-  }
-
-  private initLocalization(envs: Node[]){
-    envs.forEach((env) => {
-      this.getLocalization(env).subscribe(
-        locals => this.mergeLocalizations(locals),
-        error => this.errorMessage = <any>error);
-    })
-  }
-
-  private mergeLocalizations(locals: Localization[]){
-    let t_local = this._cache
-    locals.forEach(function(local){
-      let pos = t_local.map(function(e) { return e.name; }).indexOf(local.name);
-      if (pos == -1) {
-        t_local.push(local)
-      }
-    })
-    this._cache = t_local
-  }
-
-  private getLocalization(node: Node){
-    let url = this._nodeUrl + "/" + node.sourceName
-    if (node.sourceType === 'Environment') {
-      url = url + "/localizations?all=true"
-    } else {
-      url = url + "/" + node.type.toLowerCase() + "s/localizations"
-    }
-    return this.get(url)
-  }
-
-  getTranslation(type:string, comp: string){
-    let trans: string
-    if (comp) {
-      trans = type + "." + comp
-    } else {
-      trans = type
-    }
-    let pos = this._cache.map(function(e) { return e.name; }).indexOf(trans);
-    if (pos != -1) {
-      return this._cache[pos].localizedName
-    }
-    return trans;
-  }
-
-  private get(url: string){
-    return this.http.get(url)
-    .map(res => <Localization[]> res.json().data)
-    .catch(this.handleError);
-  }
-
-  private handleError(error: Response) {
-    console.error(error);
-    return Observable.throw(error.json().error || 'Server error');
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/localization/localization.ts b/org.eclipse.mdm.application/src/main/webapp/app/localization/localization.ts
deleted file mode 100644
index f1d8e5e..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/localization/localization.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-export class Localization {
-  name: string;
-  localizedName: string;
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/main.ts b/org.eclipse.mdm.application/src/main/webapp/app/main.ts
deleted file mode 100644
index 256f08f..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/main.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {bootstrap} from '@angular/platform-browser-dynamic';
-import {ROUTER_PROVIDERS} from '@angular/router';
-import {enableProdMode} from '@angular/core';
-import {MDMComponent} from './mdm.component';
-import 'rxjs/Rx';
-
-if (process.env.ENV === 'production') {
-  enableProdMode();
-}
-
-bootstrap(MDMComponent, [ROUTER_PROVIDERS]);
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/mdm.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/mdm.component.ts
deleted file mode 100644
index a43b5e7..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/mdm.component.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, OnInit, ViewContainerRef} from '@angular/core';
-import {HTTP_PROVIDERS} from '@angular/http';
-import {Routes, Router, ROUTER_DIRECTIVES} from '@angular/router';
-
-import {MDMMenuComponent} from './navigator/mdm-menu.component';
-
-import {NodeService} from './navigator/node.service';
-import {BasketService} from './basket/basket.service';
-import {LocalizationService} from './localization/localization.service';
-import {PropertyService} from './properties';
-import {FilereleaseService} from './filerelease/filerelease.service';
-
-@Component({
-  selector: 'mdm-web',
-  template: require('../templates/mdm.component.html'),
-  directives: [ROUTER_DIRECTIVES],
-  providers: [HTTP_PROVIDERS, NodeService, PropertyService, LocalizationService, FilereleaseService, BasketService]
-})
-@Routes([
-  {path: '/mdmmenu', component: MDMMenuComponent},
-])
-export class MDMComponent implements OnInit{
-  brand = 'openMDM5 Web';
-  _comp: string = "MDMMenu";
-  viewContainerRef;
-
-  constructor(private router: Router,
-              viewContainerRef:ViewContainerRef){
-                this.viewContainerRef = viewContainerRef;
-              }
-
-  ngOnInit(){
-    this.router.navigate(['/mdmmenu'])
-  }
-
-  activate(comp: string){
-    this._comp = comp;
-  }
-  isActive(comp: string){
-    if (comp === this._comp) {
-      return 'active';
-    }
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/navigator/mdm-menu.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/navigator/mdm-menu.component.ts
deleted file mode 100644
index bf2ff2b..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/navigator/mdm-menu.component.ts
+++ /dev/null
@@ -1,55 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, ViewEncapsulation} from '@angular/core';
-import {OnActivate, RouteSegment, Router, Routes} from '@angular/router';
-import { ACCORDION_DIRECTIVES } from 'ng2-bootstrap/ng2-bootstrap';
-
-import {Node} from './node';
-import {MDMNodeProviderComponent} from './mdm-node-provider.component';
-
-import {MDMNavigatorComponent} from './mdm-navigator.component';
-import {MDMBasketComponent} from '../basket/mdm-basket.component';
-import {MDMDetailComponent} from '../details/mdm-detail.component';
-import {MDMSearchComponent} from '../search/mdm-search.component';
-
-@Component({
-  selector: 'mdm-menu',
-  template: require('../../templates/navigator/mdm-menu.component.html'),
-  styles: [
-    '.panel-body {padding: 0px;}',
-    '.list-group {margin-bottom: 0px;}',
-    '.list-group-item:first-child {border-top-left-radius: 0px; border-top-right-radius: 0px;}',
-    '.list-group-item:last-child {border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; border-bottom-style: none;}'
-  ],
-  directives: [MDMNavigatorComponent, MDMBasketComponent, MDMDetailComponent, ACCORDION_DIRECTIVES],
-  providers: [],
-  encapsulation: ViewEncapsulation.None
-})
-@Routes([
-  {path: '/details', component: MDMDetailComponent},
-  {path: '/search', component: MDMSearchComponent}
-])
-export class MDMMenuComponent{
-  selectedNode: Node = new Node;
-  activeNode: Node;
-  closeOther:boolean = false;
-  navigator:string = "Navigation";
-  basket:string = "Warenkorb";
-
-  constructor(private router: Router){}
-
-  updateSelectedNode(node: Node) {
-    this.selectedNode = node
-  }
-  updateActiveNode(node: Node) {
-    this.activeNode = node
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/navigator/mdm-navigator.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/navigator/mdm-navigator.component.ts
deleted file mode 100644
index ff8bb77..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/navigator/mdm-navigator.component.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
-import {ACCORDION_DIRECTIVES} from 'ng2-bootstrap/ng2-bootstrap';
-
-import {Node} from './node';
-import {NodeService} from './node.service';
-
-import {MDMNodeProviderComponent} from './mdm-node-provider.component';
-
-@Component({
-  selector: 'mdm-navigator',
-  template: require('../../templates/navigator/mdm-navigator.component.html'),
-  directives: [MDMNodeProviderComponent, ACCORDION_DIRECTIVES],
-  providers: []
-})
-export class MDMNavigatorComponent implements OnInit {
-  @Output() selectingNode = new EventEmitter<Node>();
-  @Output() onActive = new EventEmitter<Node>();
-  @Input() activeNode: Node;
-
-  openNode: Node;
-  selectedNode: Node;
-  nodes: Node[];
-  errorMessage: string;
-
-  constructor(
-    private _nodeService: NodeService) {}
-
-  getNodes(){
-    let node: Node;
-    this._nodeService.getNodes(node).subscribe(
-      nodes => this.nodes = nodes,
-      error => this.errorMessage = <any>error);
-  }
-  ngOnInit(){
-    this.getNodes();
-  }
-  onOpenNode(node: Node) {
-    this.activateNode(node);
-  }
-  updateSelectedNode(node) {
-    this.selectedNode = node
-    this.activeNode = node
-    this.onActive.emit(node)
-    this.selectingNode.emit(node)
-  }
-  updateActiveNode(node){
-    this.activeNode = node
-    this.onActive.emit(node)
-  }
-  onSelectNode(node){
-    this.selectingNode.emit(node);
-    this.onActive.emit(node);
-  }
-  private activateNode(node: Node){
-    if (this.openNode === node && this.openNode.active) {
-      this.openNode.active = false;
-      return
-    }
-    if (this.openNode) {
-      this.openNode.active = false;
-    }
-    this.openNode = node;
-    this.openNode.active = true;
-  }
-  isActive(node){
-    if (this._nodeService.compareNode(this.activeNode, node)) {return "active"}
-  }
-  isOpen(node: Node){
-    if (node.active) {
-      return "glyphicon glyphicon-chevron-down"
-    } else {
-      return "glyphicon glyphicon-chevron-right"
-    }
-  }
-  getMargin(){
-    return 10;
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/navigator/mdm-node-provider.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/navigator/mdm-node-provider.component.ts
deleted file mode 100644
index 300c85b..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/navigator/mdm-node-provider.component.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
-
-import {Node} from './node';
-import {NodeService} from './node.service';
-
-@Component({
-  selector: 'mdm-node-provider',
-  template: require('../../templates/navigator/mdm-node-provider.component.html'),
-  directives: [MDMNodeProviderComponent],
-  providers: [],
-  inputs: ['rootNode', 'indent']
-})
-export class MDMNodeProviderComponent implements OnInit {
-  @Output() selectingNode = new EventEmitter<Node>();
-  @Output() onActive = new EventEmitter<Node>();
-  @Input() activeNode: Node;
-
-  rootNode: Node;
-  indent: number;
-
-  openNode: Node;
-  nodes: Node[];
-  errorMessage: string;
-  selectedNode: Node;
-
-  constructor(
-    private _nodeService: NodeService) {}
-
-  ngOnInit(){
-    this.getNodes();
-  }
-
-  getNodes(){
-    if (this.rootNode.type !== "Channel") {
-      this._nodeService.getNodes(this.rootNode).subscribe(
-        nodes => this.nodes = nodes,
-        error => this.errorMessage = <any>error);
-    }
-  }
-  onOpenNode(node:Node){
-    if (this.openNode === node && this.openNode.active) {
-      this.openNode.active = false;
-      return
-    }
-    if (this.openNode) {
-      this.openNode.active = false;
-    }
-    this.openNode = node;
-    this.openNode.active = true;
-  }
-  updateActiveNode(node){
-    this.activeNode = node
-    this.onActive.emit(node)
-  }
-  onSelectNode(node){
-    this.selectingNode.emit(node);
-  }
-  updateSelectedNode(node) {
-    this.selectedNode = node
-    this.activeNode = node
-    this.onActive.emit(node)
-    this.selectingNode.emit(node)
-  }
-  isActive(node){
-    if (this._nodeService.compareNode(this.activeNode, node)) {return "active"}
-  }
-  isOpen(node: Node){
-    if (node.active) {
-      return "glyphicon glyphicon-chevron-down"
-    } else {
-      return "glyphicon glyphicon-chevron-right"
-    }
-  }
-  getMargin(){
-    return this.indent + 10;
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/navigator/node.service.ts b/org.eclipse.mdm.application/src/main/webapp/app/navigator/node.service.ts
deleted file mode 100644
index ab6019b..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/navigator/node.service.ts
+++ /dev/null
@@ -1,106 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Injectable} from '@angular/core';
-import {Http, Response, Headers, RequestOptions} from '@angular/http';
-import {Observable} from 'rxjs/Observable';
-
-import {Node} from './node';
-import {PropertyService} from '../properties'
-
-@Injectable()
-export class NodeService {
-  constructor(private http: Http,
-              private _prop: PropertyService){
-                if (this._prop.api_host){this._nodeUrl = this._prop.api_host + this._nodeUrl}
-              }
-
-  private _nodeUrl = 'mdm/environments'
-
-  public locals
-
-  private getRootNodes(){
-    return this.http.get(this._nodeUrl)
-    .map(res => <Node[]> res.json().data)
-    .catch(this.handleError);
-  }
-
-  private getNode(url: string) {
-    return this.http.get(url)
-    .map(res => <Node[]> res.json().data)
-    .catch(this.handleError);
-  }
-
-  searchNodes(query, env, type){
-    return this.http.get(this._nodeUrl + "/" + env + "/" + type + "?" + query)
-              .map(res => <Node[]> res.json().data)
-              .catch(this.handleError);
-  }
-
-  searchFT(query, env){
-    return this.http.get(this._nodeUrl + "/" + env + "/search?q=" + query)
-              .map(res => <Node[]> res.json().data)
-              .catch(this.handleError);
-  }
-
-  getNodes(node: Node) {
-    if (node === undefined){
-      return this.getRootNodes()
-    }
-    return this.getNode(this.getUrl(node))
-  }
-
-  addNode (name: string) : Observable<Node>  {
-    let body = JSON.stringify({ name });
-    let headers = new Headers({ 'Content-Type': 'application/json' });
-    let options = new RequestOptions({ headers: headers });
-
-    return this.http.post(this._nodeUrl, body, options)
-                    .map(res =>  <Node> res.json().data)
-                    .catch(this.handleError)
-  }
-
-  deleteNode(node : Node) {
-    return this.http.delete(this.getUrl(node))
-      .map(res => <Node[]> res.json().data)
-      .catch(this.handleError);
-  }
-
-  private getUrl(node: Node) {
-    let url = this._nodeUrl + "/" + node.sourceName
-    switch(node.type)
-    {
-      case 'Environment':
-        return url + "/tests"
-      case 'Test':
-        return url + "/teststeps?filter=Test.Id eq " + node.id
-      case 'TestStep':
-        return url + "/measurements?filter=TestStep.Id eq " + node.id
-      case 'Measurement':
-        return url + "/channelgroups?filter=Measurement.Id eq " + node.id
-      case 'ChannelGroup':
-        return url + "/channels?filter=ChannelGroup.Id eq " + node.id
-      case 'Channel':
-        return
-    }
-  }
-  compareNode(node1, node2){
-    if (node1 == undefined || node2 == undefined) { return }
-    let n1 = node1.name + node1.id + node1.type + node1.sourceName
-    let n2 = node2.name + node2.id + node2.type + node2.sourceName
-    if (n1 == n2) { return true }
-    return false
-  }
-
-  private handleError(error: Response) {
-    console.error(error);
-    return Observable.throw(error.json().error || 'Server error');
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/navigator/node.ts b/org.eclipse.mdm.application/src/main/webapp/app/navigator/node.ts
deleted file mode 100644
index 35444d2..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/navigator/node.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-export class Node {
-  name: string;
-  id: number;
-  type: string;
-  sourceType: string;
-  sourceName: string;
-  attributes: attribute[];
-  active: boolean;
-}
-
-export class attribute {
-  name: string;
-  value: string;
-  unit: string;
-  dataType: string;
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/polyfills.ts b/org.eclipse.mdm.application/src/main/webapp/app/polyfills.ts
deleted file mode 100644
index 2fb29d4..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/polyfills.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import 'core-js/es6';
-import 'reflect-metadata';
-require('zone.js/dist/zone');
-if (process.env.ENV === 'production') {
-  // Production
-} else {
-  // Development
-  Error['stackTraceLimit'] = Infinity;
-  require('zone.js/dist/long-stack-trace-zone');
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/search/dynamic-form-search.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/search/dynamic-form-search.component.ts
deleted file mode 100644
index f017ba7..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/search/dynamic-form-search.component.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, Input} from '@angular/core';
-import {ControlGroup} from '@angular/common';
-import {SearchBase} from './search-base';
-import {LocalizationService} from '../localization/localization.service';
-
-@Component({
-  selector:'df-search',
-  template: require('../../templates/search/dynamic-form-search.component.html')
-})
-export class DynamicFormSearchComponent {
-  @Input() search:SearchBase<any>;
-  @Input() form:ControlGroup;
-
-  constructor(private localservice : LocalizationService) {}
-  get isValid() {
-    return this.form.controls[this.search.key].valid;
-  }
-  getTrans(label: string){
-    let a = label.split(".")
-    return this.localservice.getTranslation(a[0], a[1])
-  }
-  removeItem(item){
-    this.search.active = false;
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/search/dynamic-form.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/search/dynamic-form.component.ts
deleted file mode 100644
index bad2266..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/search/dynamic-form.component.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component, Input, OnInit, OnChanges, SimpleChange} from '@angular/core';
-import {ControlGroup} from '@angular/common';
-
-import {ACCORDION_DIRECTIVES} from 'ng2-bootstrap/ng2-bootstrap';
-
-import {SearchBase} from './search-base';
-import {SearchService} from './search.service';
-import {SearchControlService} from './search-control.service';
-import {DynamicFormSearchComponent} from './dynamic-form-search.component';
-import {Node} from '../navigator/node';
-import {NodeService} from '../navigator/node.service';
-import {BasketService} from '../basket/basket.service';
-import {LocalizationService} from '../localization/localization.service';
-
-
-@Component({
-  selector:'dynamic-form',
-  template: require('../../templates/search/dynamic-form.component.html'),
-  directives: [DynamicFormSearchComponent, ACCORDION_DIRECTIVES],
-  providers:  [SearchControlService]
-})
-export class DynamicForm implements OnChanges, OnInit{
-  @Input() groups = [];
-  @Input() env: Node[];
-  @Input() type: string;
-  form: ControlGroup;
-  nodes: Node[] = [];
-  errorMessage: string;
-
-  constructor(private scs: SearchControlService,
-              private service: NodeService,
-              private basket: BasketService,
-              private localservice : LocalizationService) {}
-
-  ngOnInit(){
-    this.form = this.scs.toControlGroup(this.groups);
-  }
-  ngOnChanges(){
-    this.form = this.scs.toControlGroup(this.groups);
-  }
-  onSubmit() {
-    this.nodes = []
-    this.getResults(this.form.value);
-  }
-
-  hasActiveItems(group) {
-    let g = this.groups.map(function(x){return x.name}).indexOf(group)
-    let i = this.groups[g].items.map(function(x){
-      return x.active
-    }).filter(function(v){
-      return v == true
-    })
-    if (i.length > 0){return true}
-    return false
-  }
-
-  getResults(values) {
-    let query = "filter="
-    for (var prop in values){
-      if (values.hasOwnProperty(prop)){
-        if (values[prop]){
-          query = query + prop + " eq " + values[prop] + " and "
-        }
-      }
-    }
-    if (query == "filter=") {
-      return
-    }
-    for (let i in this.env){
-      this.service.searchNodes(query.slice(0,-5), this.env[i].sourceName, this.type).subscribe(
-        nodes => this.nodes = this.nodes.concat(nodes),
-        error => this.errorMessage = <any>error);
-    }
-  }
-  add2Basket(node: Node){
-    this.basket.addNode(node);
-  }
-
-  getTrans(label: string){
-    return this.localservice.getTranslation(label, "")
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/search/mdm-full-text-search.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/search/mdm-full-text-search.component.ts
deleted file mode 100644
index cc0ae10..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/search/mdm-full-text-search.component.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component} from '@angular/core'
-
-import {SearchService} from './search.service';
-import {NodeService} from '../navigator/node.service';
-import {Node} from '../navigator/node';
-
-
-@Component({
-  selector: 'mdm-full-text-search',
-  template: require('../../templates/search/mdm-full-text-search.component.html'),
-  directives: [],
-  providers:  [SearchService]
-})
-export class MDMFullTextSearchComponent {
-  nodes : Node[] = []
-  envs : Node[] = []
-  errorMessage : string = ""
-  selectedEnv : Node[] = []
-
-  constructor(private service: NodeService){
-    let node: Node;
-    this.service.getNodes(node).subscribe(
-      nodes => this.setEvns(nodes),
-      error => this.errorMessage = <any>error);
-  }
-
-  setEvns(envs){
-    this.envs = envs
-    this.selectedEnv = envs.slice()
-  }
-
-  selectEnv(env: string){
-    let i = this.selectedEnv.map(function(e){return e.sourceName}).indexOf(env)
-    if (i > -1) {
-        this.selectedEnv.splice(i, 1)
-    } else {
-      let e = this.envs.map(function(e){return e.sourceName}).indexOf(env)
-      this.selectedEnv.push(this.envs[e])
-    }
-  }
-
-  onSubmit(query: string){
-    this.nodes = []
-    for (let i in this.selectedEnv) {
-      this.search(query, this.selectedEnv[i].sourceName)
-    }
-  }
-
-  search(query: string, env: string){
-    this.service.searchFT(query, env).subscribe(
-      nodes => this.nodes = this.nodes.concat(nodes),
-      error => this.errorMessage = <any>error
-    );
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/search/mdm-search.component.ts b/org.eclipse.mdm.application/src/main/webapp/app/search/mdm-search.component.ts
deleted file mode 100644
index 5c1272e..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/search/mdm-search.component.ts
+++ /dev/null
@@ -1,145 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Component} from '@angular/core'
-import {DynamicForm} from './dynamic-form.component';
-
-import {DROPDOWN_DIRECTIVES, ACCORDION_DIRECTIVES, TYPEAHEAD_DIRECTIVES} from 'ng2-bootstrap/ng2-bootstrap';
-import {MODAL_DIRECTVES, BS_VIEW_PROVIDERS} from 'ng2-bootstrap/ng2-bootstrap';
-
-import {SearchService} from './search.service';
-import {NodeService} from '../navigator/node.service';
-import {Node} from '../navigator/node';
-import {LocalizationService} from '../localization/localization.service';
-
-@Component({
-  selector: 'mdm-search',
-  template: require('../../templates/search/mdm-search.component.html'),
-  directives: [DynamicForm, DROPDOWN_DIRECTIVES, MODAL_DIRECTVES, ACCORDION_DIRECTIVES, TYPEAHEAD_DIRECTIVES],
-  providers:  [SearchService],
-  viewProviders: [BS_VIEW_PROVIDERS]
-})
-export class MDMSearchComponent {
-  definitions:any
-  ungrouped:any[] = []
-  groups:any[] = []
-  selectedGroups:any[] = []
-  envs:Node[] = []
-  selectedEnv: Node[] = []
-  type: any = {label: "Versuchen"}
-  errorMessage: string
-
-  constructor(private service: SearchService,
-              private nodeservice: NodeService,
-              private localservice: LocalizationService) {
-    this.definitions = service.getDefinitions();
-    let node: Node;
-    this.nodeservice.getNodes(node).subscribe(
-      nodes => this.setEvns(nodes),
-      error => this.errorMessage = <any>error);
-  }
-
-  setEvns(envs){
-    this.envs = envs
-    for (let i = 0; i < envs.length; i++) {
-      this.selectedEnv.push(envs[i])
-    }
-    this.selectDef(this.definitions.options[0])
-  }
-
-  selectEnv(env: string){
-    let i = this.selectedEnv.map(function(e){return e.sourceName}).indexOf(env)
-    if (i > -1) {
-        this.selectedEnv.splice(i, 1)
-    } else {
-      let e = this.envs.map(function(e){return e.sourceName}).indexOf(env)
-      this.selectedEnv.push(this.envs[e])
-    }
-    this.selectDef(this.type)
-  }
-
-  selectItem(item){
-    let a = item.key.split(".")
-    let g = this.groups.map(function(x) {return x.name; }).indexOf(a[0]);
-    let i = this.groups[g].items.indexOf(item)
-    if (this.groups[g].items[i].active) {
-      this.groups[g].items[i].active = false
-    } else {
-      this.groups[g].items[i].active = true
-    }
-  }
-
-  selectDef(type:any){
-    this.type = type
-    this.groups = []
-    this.ungrouped = []
-    for (let i = 0; i < this.selectedEnv.length; i++){
-      this.service.getSearches(type.value, this.selectedEnv[i].sourceName).then(defs => this.groupBy(defs))
-    }
-  }
-
-  public typeaheadOnSelect(e:any):void {
-    this.selectItem(e.item)
-  }
-
-  private arrayUnique(source, target) {
-    source.forEach(function(item) {
-      let i = target.map(function(x) {return x.key}).indexOf(item.key)
-      if (i != -1) {return}
-      target.push(item)
-    })
-    return target
-  }
-
-  groupBy(defs){
-    this.ungrouped = this.arrayUnique(defs, this.ungrouped)
-    this.transTypeAHead()
-    let groups = this.groups.slice()
-    defs.forEach(function(obj){
-      let str = obj.key
-      let a = str.split(".")
-      let g = groups.map(function(x) {return x.name; }).indexOf(a[0]);
-      if (g == -1) {
-        groups.push({name:a[0], items:[obj]})
-      } else {
-        let i = groups[g].items.map(function(x) {return x.key}).indexOf(obj.key)
-        if (i != -1) {return}
-        groups[g].items.push(obj)
-      }
-    })
-    this.groups = groups
-  }
-
-  isActive(item) {
-    if (item.active) { return "active"}
-    return
-  }
-
-  transTypeAHead(){
-    for(let i in this.ungrouped){
-      if(this.ungrouped[i].labelt){continue}
-      let a = this.ungrouped[i].label.split(".")
-      this.ungrouped[i].labelt = this.getTrans(a[0]) + "." + this.getTrans(this.ungrouped[i].label)
-    }
-  }
-
-  getTrans(label: string){
-    let type = "",
-    comp = ""
-    if (label.includes(".")){
-      let a = label.split(".")
-      type = a[0]
-      comp = a[1]
-    } else {
-      type = label
-    }
-    return this.localservice.getTranslation(type, comp)
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/search/search-base.ts b/org.eclipse.mdm.application/src/main/webapp/app/search/search-base.ts
deleted file mode 100644
index c05a471..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/search/search-base.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-export class SearchBase<T>{
-  value:T;
-  key:string;
-  label:string;
-  required:boolean;
-  order:number;
-  controlType:string;
-  active:boolean;
-  constructor(options:{
-      value?:T,
-      key?:string,
-      label?:string,
-      required?:boolean,
-      order?:number,
-      controlType?:string,
-      active?:boolean
-    } = {}){
-    this.value = options.value;
-    this.key = options.key || '';
-    this.label = options.label || '';
-    this.required = !!options.required;
-    this.order = options.order === undefined ? 1 : options.order;
-    this.controlType = options.controlType || '';
-    this.active = options.active || false;
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/search/search-control.service.ts b/org.eclipse.mdm.application/src/main/webapp/app/search/search-control.service.ts
deleted file mode 100644
index 007fdb6..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/search/search-control.service.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Injectable} from '@angular/core';
-import {ControlGroup, FormBuilder, Validators} from '@angular/common';
-import {SearchBase} from './search-base';
-
-@Injectable()
-export class SearchControlService {
-  constructor(private fb:FormBuilder){ }
-
-  toControlGroup(searches) {
-    let group = {};
-    for (let i = 0; i < searches.length; i++){
-      searches[i].items.forEach(search => {
-        group[search.key] = search.required ? [search.value || '', Validators.required] : [search.value || ''];
-      });
-    }
-    return this.fb.group(group);
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/search/search-dropdown.ts b/org.eclipse.mdm.application/src/main/webapp/app/search/search-dropdown.ts
deleted file mode 100644
index 2af7528..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/search/search-dropdown.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {SearchBase} from './search-base';
-
-export class DropdownSearch extends SearchBase<string>{
-  controlType = 'dropdown';
-  options:{key:string, value:string}[] = [];
-
-  constructor(options:{} = {}){
-    super(options);
-    this.options = options['options'] || [];
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/search/search-textbox.ts b/org.eclipse.mdm.application/src/main/webapp/app/search/search-textbox.ts
deleted file mode 100644
index 98edc8a..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/search/search-textbox.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {SearchBase} from './search-base';
-
-export class TextboxSearch extends SearchBase<string>{
-  controlType = 'textbox';
-  type:string;
-
-  constructor(options:{} = {}){
-    super(options);
-    this.type = options['type'] || '';
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/search/search.service.ts b/org.eclipse.mdm.application/src/main/webapp/app/search/search.service.ts
deleted file mode 100644
index e643890..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/search/search.service.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import {Injectable} from '@angular/core';
-import {Http, Response, Headers, RequestOptions} from '@angular/http';
-import {Observable} from 'rxjs/Observable';
-
-import {SearchBase} from './search-base';
-import {DynamicForm} from './dynamic-form.component';
-import {TextboxSearch} from './search-textbox';
-import {DropdownSearch} from './search-dropdown';
-
-import {PropertyService} from '../properties'
-import {Node} from '../navigator/node';
-
-class Definition {
-  attrName: string;
-  boType: string;
-  criteria: string;
-  valueType: string;
-}
-
-@Injectable()
-export class SearchService{
-
-  constructor(private http: Http,
-              private _prop: PropertyService){
-                if (this._prop.api_host){this._searchUrl = this._prop.api_host + this._searchUrl}
-              }
-
-  private _searchUrl = 'mdm/environments'
-  private errorMessage: string
-
-  private defs:Definition[]
-
-  private handleError(error: Response) {
-    console.error(error);
-    return Observable.throw(error.json().error || 'Server error');
-  }
-
-  loadDefinitions(type:string, env:string) {
-    return this.http.get(this._searchUrl + '/' + env + "/" + type + "/searchattributes")
-               .toPromise()
-               .then(response => response.json().data)
-               .catch(this.handleError)
-  }
-
-  getDefinitions() {
-    let definitions:SearchBase<any> =
-      new DropdownSearch({
-        key:'definitions',
-        label: 'definitions',
-        options: [
-          {key:'1', value:'tests', label:'Versuchen'},
-          {key:'2', value:'teststeps', label:'Versuchsschritten'},
-          {key:'3', value:'measurements', label:'Messungen'}
-        ],
-        order: 1
-      })
-    return definitions;
-  }
-
-  buildSearchForm(defs){
-    let searchesForm:SearchBase<any>[] = []
-    defs.forEach(function(def, i){
-      searchesForm.push(new TextboxSearch({
-        key: def.boType + "." + def.attrName,
-        label: def.boType + "." + def.attrName,
-        value: '',
-        required: false,
-        order: i,
-        type: def.valueType=="INTEGER"?"number":"text"
-      }))
-    })
-    return searchesForm
-  }
-
-  getSearches(type:string, env:string) {
-    if (!env) {
-      return
-    }
-    return this.loadDefinitions(type, env)
-        .then((defs) => {
-          return this.buildSearchForm(defs)
-        })
-        .catch(error => this.errorMessage = error); // TODO: Display error message
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/app/vendor.ts b/org.eclipse.mdm.application/src/main/webapp/app/vendor.ts
deleted file mode 100644
index 69816da..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/app/vendor.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-import '@angular/platform-browser';
-import '@angular/platform-browser-dynamic';
-import '@angular/core';
-import '@angular/common';
-import '@angular/http';
-import '@angular/router-deprecated';
-
-import 'rxjs';
-
-import 'ng2-bootstrap/ng2-bootstrap';
-import 'bootstrap/dist/css/bootstrap.min.css';
diff --git a/org.eclipse.mdm.application/src/main/webapp/config/helpers.js b/org.eclipse.mdm.application/src/main/webapp/config/helpers.js
deleted file mode 100644
index 630b1bf..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/config/helpers.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-var path = require('path');
-var _root = path.resolve(__dirname, '..');
-function root(args) {
-  args = Array.prototype.slice.call(arguments, 0);
-  return path.join.apply(path, [_root].concat(args));
-}
-exports.root = root;
diff --git a/org.eclipse.mdm.application/src/main/webapp/config/webpack.common.js b/org.eclipse.mdm.application/src/main/webapp/config/webpack.common.js
deleted file mode 100644
index ec0cb2d..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/config/webpack.common.js
+++ /dev/null
@@ -1,63 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-var webpack = require('webpack');
-var HtmlWebpackPlugin = require('html-webpack-plugin');
-var ExtractTextPlugin = require('extract-text-webpack-plugin');
-var helpers = require('./helpers');
-
-module.exports = {
-  entry: {
-    'polyfills': './app/polyfills.ts',
-    'vendor': './app/vendor.ts',
-    'app': './app/main.ts'
-  },
-
-  resolve: {
-    extensions: ['', '.js', '.ts']
-  },
-
-  module: {
-    loaders: [
-      {
-        test: /\.ts$/,
-        loader: 'ts'
-      },
-      {
-        test: /\.html$/,
-        loader: 'html'
-      },
-      {
-        test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
-        loader: 'file?name=assets/[name].[hash].[ext]'
-      },
-      {
-        test: /\.css$/,
-        exclude: helpers.root('src', 'app'),
-        loader: ExtractTextPlugin.extract('style', 'css?sourceMap')
-      },
-      {
-        test: /\.css$/,
-        include: helpers.root('src', 'app'),
-        loader: 'raw'
-      }
-    ]
-  },
-
-  plugins: [
-    new webpack.optimize.CommonsChunkPlugin({
-      name: ['app', 'vendor', 'polyfills']
-    }),
-
-    new HtmlWebpackPlugin({
-      template: 'index.html'
-    })
-  ]
-};
diff --git a/org.eclipse.mdm.application/src/main/webapp/config/webpack.dev.js b/org.eclipse.mdm.application/src/main/webapp/config/webpack.dev.js
deleted file mode 100644
index 3e39cfc..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/config/webpack.dev.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-var webpackMerge = require('webpack-merge');
-var ExtractTextPlugin = require('extract-text-webpack-plugin');
-var commonConfig = require('./webpack.common.js');
-var helpers = require('./helpers');
-
-module.exports = webpackMerge(commonConfig, {
-  devtool: 'cheap-module-eval-source-map',
-  context: helpers.root(''),
-  output: {
-    path: helpers.root('dist'),
-    publicPath: 'http://localhost:9090/dist/',
-    filename: '[name].js',
-    chunkFilename: '[id].chunk.js'
-  },
-
-  plugins: [
-    new ExtractTextPlugin('[name].css')
-  ],
-
-  devServer: {
-    historyApiFallback: true,
-    stats: 'minimal'
-  }
-});
diff --git a/org.eclipse.mdm.application/src/main/webapp/config/webpack.prod.js b/org.eclipse.mdm.application/src/main/webapp/config/webpack.prod.js
deleted file mode 100644
index f8086ee..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/config/webpack.prod.js
+++ /dev/null
@@ -1,46 +0,0 @@
-// *******************************************************************************
-//   * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-//   * All rights reserved. This program and the accompanying materials
-//   * are made available under the terms of the Eclipse Public License v1.0
-//   * which accompanies this distribution, and is available at
-//   * http://www.eclipse.org/legal/epl-v10.html
-//   *
-//   * Contributors:
-//   * Dennis Schroeder - initial implementation
-//   *******************************************************************************
-var webpack = require('webpack');
-var encodingPlugin = require('webpack-encoding-plugin');
-var webpackMerge = require('webpack-merge');
-var ExtractTextPlugin = require('extract-text-webpack-plugin');
-var commonConfig = require('./webpack.common.js');
-var helpers = require('./helpers');
-
-const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
-
-module.exports = webpackMerge(commonConfig, {
-  devtool: 'source-map',
-  context: helpers.root(''),
-  output: {
-    path: helpers.root('dist'),
-    publicPath: '/org.eclipse.mdm.nucleus/',
-    filename: '[name].[hash].js',
-    chunkFilename: '[id].[hash].chunk.js'
-  },
-
-  htmlLoader: {
-    minimize: false
-  },
-
-  plugins: [
-    new webpack.NoErrorsPlugin(),
-    new encodingPlugin('utf8'),
-    new webpack.optimize.DedupePlugin(),
-    new webpack.optimize.UglifyJsPlugin(),
-    new ExtractTextPlugin('[name].[hash].css'),
-    new webpack.DefinePlugin({
-      'process.env': {
-        'ENV': JSON.stringify(ENV)
-      }
-    })
-  ]
-});
diff --git a/org.eclipse.mdm.application/src/main/webapp/e2e/app.e2e-spec.ts b/org.eclipse.mdm.application/src/main/webapp/e2e/app.e2e-spec.ts
new file mode 100644
index 0000000..430cd2e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/e2e/app.e2e-spec.ts
@@ -0,0 +1,14 @@
+import { WebappPage } from './app.po';
+
+describe('webapp App', function() {
+  let page: WebappPage;
+
+  beforeEach(() => {
+    page = new WebappPage();
+  });
+
+  it('should display message saying app works', () => {
+    page.navigateTo();
+    expect(page.getParagraphText()).toEqual('app works!');
+  });
+});
diff --git a/org.eclipse.mdm.application/src/main/webapp/e2e/app.po.ts b/org.eclipse.mdm.application/src/main/webapp/e2e/app.po.ts
new file mode 100644
index 0000000..6259575
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/e2e/app.po.ts
@@ -0,0 +1,11 @@
+import { browser, element, by } from 'protractor';
+
+export class WebappPage {
+  navigateTo() {
+    return browser.get('/');
+  }
+
+  getParagraphText() {
+    return element(by.css('app-root h1')).getText();
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/e2e/tsconfig.json b/org.eclipse.mdm.application/src/main/webapp/e2e/tsconfig.json
new file mode 100644
index 0000000..656bdb1
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/e2e/tsconfig.json
@@ -0,0 +1,16 @@
+{
+  "compileOnSave": false,
+  "compilerOptions": {
+    "declaration": false,
+    "emitDecoratorMetadata": true,
+    "experimentalDecorators": true,
+    "module": "commonjs",
+    "moduleResolution": "node",
+    "outDir": "../dist/out-tsc-e2e",
+    "sourceMap": true,
+    "target": "es5",
+    "typeRoots": [
+      "../node_modules/@types"
+    ]
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/index.html b/org.eclipse.mdm.application/src/main/webapp/index.html
deleted file mode 100644
index 38db55d..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/index.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<!DOCTYPE html>
-<html>
-  <head>
-    <base href=".">
-    <title>openMDM5 Web</title>
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <meta charset="utf-8">
-  </head>
-  <body>
-    <mdm-web>Loading...</mdm-web>
-  </body>
-</html>
diff --git a/org.eclipse.mdm.application/src/main/webapp/karma.conf.js b/org.eclipse.mdm.application/src/main/webapp/karma.conf.js
new file mode 100644
index 0000000..56a922e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/karma.conf.js
@@ -0,0 +1,59 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/0.13/config/configuration-file.html
+
+module.exports = function (config) {
+  config.set({
+    basePath: '',
+    frameworks: ['jasmine', '@angular/cli'],
+    plugins: [
+      require('karma-jasmine'),
+      require('karma-chrome-launcher'),
+      require('karma-phantomjs-launcher'),
+      require('karma-jasmine-html-reporter'),
+      require('karma-coverage-istanbul-reporter'),
+      require('karma-mocha-reporter'),
+      require('@angular/cli/plugins/karma')
+    ],
+    client:{
+      clearContext: false // leave Jasmine Spec Runner output visible in browser
+    },
+    files: [
+      { pattern: './src/test.ts', watched: false }
+    ],
+    preprocessors: {
+      './src/test.ts': ['@angular/cli']
+    },
+    mime: {
+      'text/x-typescript': ['ts','tsx']
+    },
+    coverageIstanbulReporter: {
+      reports: [ 'html', 'lcovonly' ],
+      fixWebpackSourcePaths: true
+    },
+    angularCli: {
+      config: './angular-cli.json',
+      environment: 'dev'
+    },
+    reporters: config.angularCli && config.angularCli.codeCoverage
+              ? ['progress', 'coverage-istanbul']
+              : ['progress', 'kjhtml'],
+    port: 9876,
+    colors: true,
+    logLevel: config.LOG_INFO,
+    autoWatch: true,
+    browsers: ['Chrome', 'PhantomJS'],
+    singleRun: false
+  });
+};
diff --git a/org.eclipse.mdm.application/src/main/webapp/login.css b/org.eclipse.mdm.application/src/main/webapp/login.css
deleted file mode 100644
index bbbe469..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/login.css
+++ /dev/null
@@ -1,45 +0,0 @@
-body {
-	padding-top: 40px;
-	padding-bottom: 40px;
-	background-color: #eee;
-}
-
-.form-signin {
-	max-width: 330px;
-	padding: 15px;
-	margin: 0 auto;
-}
-
-.form-signin .form-signin-heading, .form-signin .checkbox {
-	margin-bottom: 10px;
-}
-
-.form-signin .checkbox {
-	font-weight: normal;
-}
-
-.form-signin .form-control {
-	position: relative;
-	height: auto;
-	-webkit-box-sizing: border-box;
-	-moz-box-sizing: border-box;
-	box-sizing: border-box;
-	padding: 10px;
-	font-size: 16px;
-}
-
-.form-signin .form-control:focus {
-	z-index: 2;
-}
-
-.form-signin input[type="email"] {
-	margin-bottom: -1px;
-	border-bottom-right-radius: 0;
-	border-bottom-left-radius: 0;
-}
-
-.form-signin input[type="password"] {
-	margin-bottom: 10px;
-	border-top-left-radius: 0;
-	border-top-right-radius: 0;
-}
\ No newline at end of file
diff --git a/org.eclipse.mdm.application/src/main/webapp/login.jsp b/org.eclipse.mdm.application/src/main/webapp/login.jsp
deleted file mode 100644
index 16c8953..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/login.jsp
+++ /dev/null
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-<meta charset="utf-8">
-<meta http-equiv="X-UA-Compatible" content="IE=edge">
-<meta name="viewport" content="width=device-width, initial-scale=1">
-<meta name="description" content="">
-<meta name="author" content="">
-
-<title>MDM Web Login</title>
-
-<link rel='stylesheet' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'>
-<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/login.css">
-
-
-</head>
-
-<body>
-	<div class="bform">
-		<div class="bform2">
-			<div class="container">
-
-				<form class="form-signin" method=post action="j_security_check">
-					<h2 class="form-signin-heading">MDM Web Login</h2>
-					<input type="text" class="form-control" name="j_username"
-						placeholder="Username" required autofocus> <input
-						type="password" class="form-control" name="j_password"
-						placeholder="Password" required>
-
-					<button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
-				</form>
-
-			</div>
-		</div>
-	</div>
-
-</body>
-</html>
diff --git a/org.eclipse.mdm.application/src/main/webapp/package.json b/org.eclipse.mdm.application/src/main/webapp/package.json
index 3e0b768..731f641 100644
--- a/org.eclipse.mdm.application/src/main/webapp/package.json
+++ b/org.eclipse.mdm.application/src/main/webapp/package.json
@@ -1,52 +1,65 @@
 {
   "name": "openMDM-Web",
   "version": "1.0.0",
+  "description": "MDM web client",
+  "repository": "http://git.eclipse.org/c/mdmbl/org.eclipse.mdm.nucleus.git",
+  "license": "EPL-1.0",
+  "angular-cli": {},
   "scripts": {
-    "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
-    "lite": "lite-server",
-    "postinstall": "typings install",
-    "tsc": "tsc",
-    "tsc:w": "tsc -w",
-    "typings": "typings"
+    "ng": "ng",
+    "start": "ng serve --proxy proxies/development.config.json",
+    "build": "ng build",
+    "test": "ng test --reporters mocha --browsers PhantomJS",
+    "lint": "ng lint --force --format json > coverage/tslint_out.json",
+    "e2e": "ng e2e",
+    "coverage": "ng test --browsers PhantomJS --singleRun --code-coverage",
+    "ci_build": "npm run coverage && npm run lint && ng build --base-href /org.eclipse.mdm.nucleus/",
+    "postinstall": "rimraf node_modules/ng2-dropdown-multiselect/node_modules"
   },
+  "private": true,
   "dependencies": {
-    "@angular/common":  "2.0.0-rc.1",
-    "@angular/compiler":  "2.0.0-rc.1",
-    "@angular/core":  "2.0.0-rc.1",
-    "@angular/http":  "2.0.0-rc.1",
-    "@angular/platform-browser":  "2.0.0-rc.1",
-    "@angular/platform-browser-dynamic":  "2.0.0-rc.1",
-    "@angular/router":  "2.0.0-rc.1",
-    "@angular/router-deprecated":  "2.0.0-rc.1",
-    "@angular/upgrade":  "2.0.0-rc.1",
-
-    "core-js": "^2.4.0",
-    "systemjs": "0.19.27",
-    "es6-shim": "^0.35.0",
+    "@angular/common": "^2.4.8",
+    "@angular/compiler": "^2.4.8",
+    "@angular/core": "^2.4.8",
+    "@angular/forms": "^2.4.8",
+    "@angular/http": "^2.4.8",
+    "@angular/platform-browser": "^2.4.8",
+    "@angular/platform-browser-dynamic": "^2.4.8",
+    "@angular/router": "^3.4.8",
+    "@types/file-saver": "0.0.0",
+    "bootstrap": "^3.3.7",
+    "class-transformer": "0.1.6",
+    "core-js": "^2.4.1",
+    "file-saver": "^1.3.3",
+    "font-awesome": "^4.7.0",
+    "ng2-bootstrap": "1.3.1",
+    "ng2-dropdown-multiselect": "1.1.1",
+    "ng2-split-pane": "1.3.1",
+    "primeng": "^2.0.5",
     "reflect-metadata": "^0.1.3",
-    "rxjs": "5.0.0-beta.6",
-    "zone.js": "^0.6.12",
-
-    "angular2-in-memory-web-api": "0.0.7",
-    "bootstrap": "^3.3.6",
-    "ng2-bootstrap": "1.0.17"
+    "rxjs": "^5.1.0",
+    "ts-helpers": "^1.1.1",
+    "zone.js": "^0.7.6"
   },
   "devDependencies": {
-    "typescript": "^1.8.10",
-    "typings":"^0.8.1",
-    "webpack": "^1.12.14",
-    "webpack-dev-server": "^1.14.1",
-    "webpack-merge": "^0.9.0",
-    "style-loader": "^0.13.1",
-    "ts-loader": "^0.8.1",
-    "css-loader": "^0.23.1",
-    "extract-text-webpack-plugin": "^1.0.1",
-    "file-loader": "^0.8.5",
-    "html-loader": "^0.4.3",
-    "html-webpack-plugin": "^2.15.0",
-    "null-loader": "^0.1.1",
-    "phantomjs-prebuilt": "^2.1.7",
-    "raw-loader": "^0.5.1",
-    "webpack-encoding-plugin": "^0.0.2"
+    "@angular/cli": "^1.0.0-rc.1",
+    "@angular/compiler-cli": "^2.4.8",
+    "@types/jasmine": "2.5.38",
+    "@types/node": "^6.0.60",
+    "codelyzer": "~2.0.0",
+    "jasmine-core": "~2.5.2",
+    "jasmine-spec-reporter": "~3.2.0",
+    "karma": "~1.4.1",
+    "karma-chrome-launcher": "~2.0.0",
+    "karma-cli": "~1.0.1",
+    "karma-coverage-istanbul-reporter": "^0.2.0",
+    "karma-jasmine": "~1.1.0",
+    "karma-jasmine-html-reporter": "^0.2.2",
+    "karma-mocha-reporter": "~2.0.0",
+    "karma-phantomjs-launcher": "~1.0.2",
+    "protractor": "~5.1.0",
+    "ts-node": "~2.0.0",
+    "tslint": "~4.4.2",
+    "typescript": "~2.0.3"
   }
 }
diff --git a/org.eclipse.mdm.application/src/main/webapp/protractor.conf.js b/org.eclipse.mdm.application/src/main/webapp/protractor.conf.js
new file mode 100644
index 0000000..fb73bf3
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/protractor.conf.js
@@ -0,0 +1,42 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
+
+const { SpecReporter } = require('jasmine-spec-reporter');
+
+exports.config = {
+  allScriptsTimeout: 11000,
+  specs: [
+    './e2e/**/*.e2e-spec.ts'
+  ],
+  capabilities: {
+    'browserName': 'chrome'
+  },
+  directConnect: true,
+  baseUrl: 'http://localhost:4200/',
+  framework: 'jasmine',
+  jasmineNodeOpts: {
+    showColors: true,
+    defaultTimeoutInterval: 30000,
+    print: function() {}
+  },
+  beforeLaunch: function() {
+    require('ts-node').register({
+      project: 'e2e/tsconfig.e2e.json'
+    });
+  },
+  onPrepare() {
+    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
+  }
+};
diff --git a/org.eclipse.mdm.application/src/main/webapp/proxies/development.config.json b/org.eclipse.mdm.application/src/main/webapp/proxies/development.config.json
new file mode 100644
index 0000000..5b5336f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/proxies/development.config.json
@@ -0,0 +1,6 @@
+{

+  "/org.eclipse.mdm.nucleus/": {

+    "target": "http://sa:sa@localhost:8080/",

+    "secure": "false"

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-modules.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-modules.component.html
new file mode 100644
index 0000000..9e57e6d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-modules.component.html
@@ -0,0 +1,31 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<nav class="navbar navbar-default">

+  <div class="container-fluid">

+    <div class="navbar-header">

+      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-admin-navbar" aria-expaned="false">

+        <span class="icon-bar"></span>

+        <span class="icon-bar"></span>

+        <span class="icon-bar"></span>

+      </button>

+      <a class="navbar-brand">{{brand}}</a>

+    </div>

+    <div class="collapse navbar-collapse" id="bs-admin-navbar">

+      <ul class="nav navbar-nav">

+        <li *ngFor="let m of links" [routerLinkActive]="['active']"><a [routerLink]="['/administration',m.path]" style="cursor:pointer;"> {{m.name}}</a></li>

+      </ul>

+    </div>

+  </div>

+</nav>

+

+<router-outlet></router-outlet>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-modules.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-modules.component.ts
new file mode 100644
index 0000000..c4bd6f0
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-modules.component.ts
@@ -0,0 +1,30 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+import {Component} from '@angular/core';
+import {Router} from '@angular/router';
+
+@Component({
+  selector: 'admin-modules',
+  templateUrl: 'admin-modules.component.html',
+  providers: []
+})
+export class AdminModulesComponent {
+
+  readonly brand = 'Geltungsbereich';
+  links = [
+    { name: 'System', path: 'system'},
+    { name: 'Quelle', path: 'source'},
+    { name: 'Benutzer', path: 'user'}
+  ];
+  constructor(private router: Router) {}
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-routing.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-routing.module.ts
new file mode 100644
index 0000000..42e21de
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-routing.module.ts
@@ -0,0 +1,37 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { PreferenceComponent } from './preference.component';
+
+const moduleRoutes: Routes = [
+  {
+    path: '',
+    children: [
+      { path: '', redirectTo: 'system', pathMatch: 'full' },
+      { path: ':scope', component: PreferenceComponent }
+    ]
+  }
+];
+
+@NgModule({
+  imports: [
+    RouterModule.forChild(moduleRoutes)
+  ],
+  exports: [
+    RouterModule
+  ]
+})
+
+export class AdminRoutingModule { }
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin.module.ts
new file mode 100644
index 0000000..e88d6aa
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin.module.ts
@@ -0,0 +1,45 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+import { FormsModule, ReactiveFormsModule } from '@angular/forms';

+import { ComponentLoaderFactory } from 'ng2-bootstrap/component-loader';

+

+import { PreferenceService } from '../core/preference.service';

+import { MDMCoreModule } from '../core/mdm-core.module';

+

+import { AdminModulesComponent } from './admin-modules.component';

+import { AdminRoutingModule } from './admin-routing.module';

+import { PreferenceComponent } from './preference.component';

+import { EditPreferenceComponent } from './edit-preference.component';

+

+@NgModule( {

+    imports: [

+        AdminRoutingModule,

+        MDMCoreModule,

+        FormsModule,

+        ReactiveFormsModule

+    ],

+    declarations: [

+        PreferenceComponent,

+        EditPreferenceComponent,

+        AdminModulesComponent,

+    ],

+    exports: [

+        AdminModulesComponent,

+    ],

+    providers: [

+        ComponentLoaderFactory,

+        PreferenceService

+    ],

+})

+export class AdminModule { }

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.css b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.css
new file mode 100644
index 0000000..a37e7d9
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.css
@@ -0,0 +1,18 @@
+@charset "ISO-8859-1";

+

+ul {

+  list-style-type: none;

+}

+textarea {

+    resize: vertical;

+}

+

+.input-group-addon {

+    min-width: 100px;

+    text-align: left;

+    font-weight: 600;

+}

+

+input[readonly] {

+     cursor: default;

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.html
new file mode 100644
index 0000000..cba7035
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.html
@@ -0,0 +1,66 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<div bsModal #lgModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">

+  <div class="modal-dialog modal-lg">

+    <div class="modal-content">

+      <div class="modal-header">

+        <button type="button" title={{TtlClose}} class="close" (click)="closeDialog()" aria-label="Close">

+          <span aria-hidden="true">&times;</span>

+        </button>

+        <h4 class="modal-title">{{LblPreferenceEditor}}</h4>

+      </div>

+      <div class="modal-body">

+        <form [formGroup]="preferenceForm" novalidate>

+          <ul class="list-group">

+            <li>

+              <div class="block">

+                <label>{{LblKey}}: </label>

+                <input type="text" class="form-control" formControlName="key" [readonly]="!isKeyEmpty">

+              </div>

+            </li>

+            <li>

+              <label>{{LblScope}}: </label>

+              <input type="text" class="form-control" formControlName="scope" [readonly]="true">

+            </li>

+            <li *ngIf="showSource">

+              <label>{{LblSource}}: </label>

+              <select class="form-control" formControlName="source">

+                <option *ngFor="let env of envs">{{env.sourceName}}</option>

+              </select>

+            </li>

+            <li *ngIf="showUser">

+              <label>{{LblUser}}: </label>

+              <input type="text" class="form-control" formControlName="user" [readonly]=true placeholder="auto-generated">

+            </li>

+            <li>

+              <div class="form-group">

+                <label for="comment">{{LblValue}}:</label>

+                <textarea class="form-control" formControlName="value" rows="5" id="value"></textarea>

+              </div>

+            </li>

+          </ul>

+          <div class="row" style="margin-top: 20px;">

+            <div class="col-md-12">

+              <button type="button" class="btn btn-default" (click)="closeDialog()">

+                <span class="glyphicon glyphicon-remove"></span> {{LblCancel}}

+              </button>

+              <button type="button" class="btn btn-default pull-right" (click)="onSave()" [disabled]="!preferenceForm.valid">

+                <span class="glyphicon glyphicon-save"></span> {{LblSave}}

+              </button>

+            </div>

+          </div>

+        </form>

+      </div>

+    </div>

+  </div>

+</div>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.ts
new file mode 100644
index 0000000..9bae3c6
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.ts
@@ -0,0 +1,115 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+import { Component, Input, Output, ViewChild, EventEmitter, OnInit } from '@angular/core';
+import { FormGroup, FormControl, FormBuilder, FormArray, Validators } from '@angular/forms';
+
+import { ModalDirective } from 'ng2-bootstrap';
+
+import { PreferenceService, Preference, Scope } from '../core/preference.service';
+import { NodeService } from '../navigator/node.service';
+import { Node } from '../navigator/node';
+
+@Component( {
+    selector: 'edit-preference',
+    templateUrl: './edit-preference.component.html',
+    styleUrls: ['./edit-preference.component.css']
+})
+export class EditPreferenceComponent implements OnInit {
+
+    readonly LblCancel = 'Abbrechen';
+    readonly LblKey = 'Schlüssel';
+    readonly LblPreferenceEditor = 'Einstellungseditor';
+    readonly LblSave = 'Speichern';
+    readonly LblScope = 'Geltungsbereich';
+    readonly LblSource = 'Quelle';
+    readonly LblUser = 'Benutzer';
+    readonly LblValue = 'Wert';
+    readonly TtlClose = 'Schließen';
+
+    @Input() scope: string;
+    showSource: boolean;
+    showUser: boolean;
+    isKeyEmpty: boolean;
+    isUserEmpty: boolean;
+    preferenceForm: FormGroup;
+    needSave = false;
+    envs: Node[];
+    errorMessage = 'Environment konnte nicht geladen werden.';
+
+    @ViewChild( 'lgModal' ) public childModal: ModalDirective;
+
+    constructor( private formBuilder: FormBuilder,
+                 private nodeService: NodeService ) { }
+
+    ngOnInit() {
+        let node: Node;
+        this.nodeService.getNodes(node).subscribe(
+                env => this.envs = env,
+                error => this.errorMessage = <any>error
+                );
+        this.setupForm( new Preference() );
+    }
+
+    setupForm( preference: Preference ) {
+        this.setOptions(preference);
+        this.preferenceForm = this.formBuilder
+            .group( {
+                scope: [preference.scope],
+                source: [preference.source],
+                user: [preference.user],
+                key: [preference.key, Validators.required],
+                value: [preference.value, Validators.required],
+                id: [preference.id]
+            });
+    }
+
+    setOptions(preference: Preference) {
+        this.needSave = false;
+        this.isKeyEmpty = preference.key === '';
+        switch ( this.scope ) {
+        case Scope.SYSTEM:
+            this.showSource = false;
+            this.showUser = false;
+            break;
+        case Scope.SOURCE:
+            this.showSource = true;
+            this.showUser = false;
+            break;
+        case Scope.USER:
+            this.showSource = false;
+            this.showUser = true;
+            break;
+        }
+    }
+
+    showDialog( preference?: Preference) {
+        if (preference == null) {
+            preference = new Preference();
+            preference.scope = this.scope;
+            if (this.scope === Scope.SOURCE) {
+                preference.source = this.envs[0].sourceName;
+            }
+        }
+        this.setupForm( preference);
+        this.childModal.show();
+    }
+
+    onSave() {
+        this.needSave = true;
+        this.childModal.hide();
+    }
+
+    closeDialog() {
+        this.childModal.hide();
+    }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.css b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.css
new file mode 100644
index 0000000..6564b1e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.css
@@ -0,0 +1,38 @@
+@charset "ISO-8859-1";

+

+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+.form-group {

+  margin-bottom: 0;

+  padding-right: 0;

+ 	padding-left: 0;

+}

+

+.form-control {

+  border-radius: 0;

+}

+

+.btn {

+  border-radius: 0;

+}

+

+ul {

+  list-style-type: none;

+}

+

+#truncatelongtexts {

+  width: 100%;

+  white-space: nowrap;

+  overflow: hidden;

+  text-overflow: ellipsis;

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.html
new file mode 100644
index 0000000..7e0fefd
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.html
@@ -0,0 +1,48 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<table class="table table-bordered" style="table-layout: fixed;">

+  <tr>

+    <th class="col-lg-5">{{LblKey}}</th>

+    <th class="col-lg-7">{{LblValue}}</th>

+    <th style="width: 60px;"></th>

+    <th style="width: 60px;"></th>

+  </tr>

+  <tr *ngFor="let preference of preferences">

+    <td>

+      <div id="truncatelongtexts">

+        {{preference.key}}

+      </div>

+    </td>

+    <td>

+      <div id="truncatelongtexts">

+        {{preference.value}}

+      </div>

+    </td>

+    <td>

+      <button type="button" class="btn btn-default" (click)="editPreference(preference)" title="{{TtlEdit}}">

+		      <span class="glyphicon glyphicon-edit"></span>

+	    </button>

+    </td>

+    <td>

+      <button type="button" class="btn btn-default" (click)="removePreference(preference)" title="{{TtlDelete}}">

+		    <span class="glyphicon glyphicon-remove"></span>

+	    </button>

+    </td>

+  </tr>

+</table>

+<div class="col-lg-12">

+  <button type="button" class="btn btn-default pull-right" (click)="editPreference()">

+  	<span class="glyphicon glyphicon-plus"></span> {{LblAddPreference}}

+  </button>

+</div>

+<edit-preference [scope]="scope"></edit-preference>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.ts
new file mode 100644
index 0000000..5ebcedb
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.ts
@@ -0,0 +1,95 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+import { Component, OnInit, ViewChild, Input, OnDestroy } from '@angular/core';
+import { FormGroup, FormControl, FormBuilder, FormArray, Validators } from '@angular/forms';
+import { ActivatedRoute } from '@angular/router';
+
+import { PreferenceService, Preference, Scope } from '../core/preference.service';
+import { EditPreferenceComponent } from './edit-preference.component';
+
+
+@Component( {
+    selector: 'mdm-preference',
+    templateUrl: './preference.component.html',
+    styleUrls: ['./preference.component.css']
+})
+export class PreferenceComponent implements OnInit, OnDestroy {
+
+  readonly LblAddPreference = 'Einstellung hinzufügen';
+  readonly LblKey = 'Schlüssel';
+  readonly LblValue = 'Wert';
+  readonly TtlEdit = 'Bearbeiten';
+  readonly TtlDelete = 'Entfernen';
+
+  @Input() preferences: Preference[];
+  scope: string;
+  subscription: any;
+  sub: any;
+
+  @ViewChild( EditPreferenceComponent )
+  private editPreferenceComponent: EditPreferenceComponent;
+
+  constructor( private formBuilder: FormBuilder,
+               private preferenceService: PreferenceService,
+               private route: ActivatedRoute) { }
+
+  ngOnInit() {
+      this.sub = this.route.params.subscribe( params => this.onScopeChange(params) );
+  }
+
+  ngOnDestroy() {
+      this.sub.unsubscribe();
+  }
+
+  onScopeChange(params: any) {
+      this.scope = params['scope'].toUpperCase();
+      this.preferenceService.getPreferenceForScope(this.scope)
+        .subscribe(pref => this.preferences = pref);
+  }
+
+  editPreference( preference?: Preference ) {
+      this.subscription = this.editPreferenceComponent.childModal.onHide.subscribe(() => this.handleDialogResult() );
+      this.editPreferenceComponent.showDialog( preference );
+  }
+
+  removePreference( preference: Preference ) {
+      if (preference.id) {
+        this.preferenceService.deletePreference( preference.id )
+        .subscribe();
+      }
+      let index = this.preferences.findIndex( p => p === preference );
+      this.preferences.splice( index, 1 );
+  }
+
+  handleDialogResult() {
+      if ( !this.editPreferenceComponent.needSave ) {
+          return;
+      }
+
+      let preference = this.editPreferenceComponent.preferenceForm.value;
+      let index = this.preferences.findIndex( p => this.preferenceEqualsWithoutId( p, preference ) );
+
+      this.preferenceService.savePreference( preference )
+          .subscribe(() => index !== -1 ? this.preferences[index] = preference : this.reloadPreference( preference ));
+      this.subscription.unsubscribe();
+  }
+
+  reloadPreference( preference: Preference ) {
+      this.preferenceService.getPreferenceForScope( preference.scope, preference.key )
+          .subscribe(p => this.preferences.push(p[0]));
+  }
+
+  preferenceEqualsWithoutId( pref1: Preference, pref2: Preference ) {
+      return pref1.key === pref2.key && pref1.source === pref2.source && pref1.user === pref2.user;
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/app-routing.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/app-routing.module.ts
new file mode 100644
index 0000000..a8211d3
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/app-routing.module.ts
@@ -0,0 +1,33 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule, Component } from '@angular/core';

+import { RouterModule, Routes } from '@angular/router';

+

+import { MDMNavigatorViewComponent } from './navigator-view/mdm-navigator-view.component';

+import { AdminModulesComponent } from './administration/admin-modules.component';

+

+const appRoutes: Routes = [

+  { path: '', redirectTo: 'navigator', pathMatch: 'full' },

+  { path: 'navigator', component: MDMNavigatorViewComponent, loadChildren: './modules/mdm-modules.module#MDMModulesModule' },

+  { path: 'administration', component: AdminModulesComponent, loadChildren: './administration/admin.module#AdminModule' }

+];

+

+@NgModule({

+  imports: [

+    RouterModule.forRoot(appRoutes)

+  ],

+  exports: [

+    RouterModule

+  ]

+})

+export class AppRoutingModule {}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/app.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/app.component.html
new file mode 100644
index 0000000..ec93e79
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/app.component.html
@@ -0,0 +1,30 @@
+<!--****************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*****************************************************************************-->
+
+<nav class="navbar navbar-default navbar-fixed-top navbar-inverse">
+  <div class="container-fluid">
+    <div class="collapse navbar-collapse" id="bs-mdm-navbar">
+      <ul class="nav navbar-nav">
+        <li *ngFor="let m of links" [routerLinkActive]="['active']"><a routerLink="{{m.path}}" style="cursor:pointer;"> {{m.name}}</a></li>
+      </ul>
+      <ul class="nav navbar-nav navbar-right">
+        <li><a href="mdm/logout"><span class="glyphicon glyphicon-log-in"></span> {{TtlLogout}}</a></li>
+      </ul>
+    </div>
+  </div>
+</nav>
+
+<div>
+  <router-outlet></router-outlet>
+</div>
+
+<mdm-notifications></mdm-notifications>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/app.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/app.component.ts
new file mode 100644
index 0000000..59ae138
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/app.component.ts
@@ -0,0 +1,26 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-root',
+  templateUrl: './app.component.html'
+})
+export class AppComponent {
+
+  readonly TtlLogout = 'Logout';
+  links = [
+      { name: 'openMDM5 Web', path: '/navigator' },
+      { name: 'Administration', path: '/administration' }
+  ];
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/app.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/app.module.ts
new file mode 100644
index 0000000..837acdc
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/app.module.ts
@@ -0,0 +1,65 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { BrowserModule } from '@angular/platform-browser';

+import { NgModule } from '@angular/core';

+import { FormsModule } from '@angular/forms';

+import { HttpModule } from '@angular/http';

+

+import { AppRoutingModule } from './app-routing.module';

+

+import { MDMCoreModule} from './core/mdm-core.module';

+import { MDMNavigatorViewModule } from './navigator-view/mdm-navigator-view.module';

+import { AdminModule } from './administration/admin.module';

+

+import { AppComponent } from './app.component';

+

+import {NodeService} from './navigator/node.service';

+import {BasketService} from './basket/basket.service';

+import {LocalizationService} from './localization/localization.service';

+import {FilereleaseService} from './filerelease/filerelease.service';

+import {NavigatorService} from './navigator/navigator.service';

+import {QueryService} from './tableview/query.service';

+import {ViewService} from './tableview/tableview.service';

+import {NodeproviderService} from './navigator/nodeprovider.service';
+import { SearchattributeTreeComponent } from './searchattribute-tree/searchattribute-tree.component';

+import { MDMNotificationService } from './core/mdm-notification.service';

+

+@NgModule({

+  imports: [

+    BrowserModule,

+    HttpModule,

+    FormsModule,

+    AppRoutingModule,

+    MDMCoreModule,

+    MDMNavigatorViewModule,

+    AdminModule

+  ],

+  declarations: [

+    AppComponent,

+  ],

+  providers: [

+    NodeService,

+    LocalizationService,

+    FilereleaseService,

+    BasketService,

+    NavigatorService,

+    QueryService,

+    NodeproviderService,

+    MDMNotificationService,

+    ViewService

+  ],

+  bootstrap: [AppComponent]

+})

+export class AppModule {

+

+    }

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/basket.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/basket.service.ts
new file mode 100644
index 0000000..694f765
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/basket.service.ts
@@ -0,0 +1,108 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Injectable, EventEmitter} from '@angular/core';
+
+import {Type, Exclude, plainToClass, serialize, deserialize} from 'class-transformer';
+
+import {MDMItem} from '../core/mdm-item';
+import {PreferenceService, Preference, Scope} from '../core/preference.service';
+
+export class Basket {
+  name: string;
+  @Type(() => MDMItem)
+  items: MDMItem[] = [];
+
+  constructor(name: string, items: MDMItem[]) {
+    this.name = name;
+    this.items = items;
+  }
+}
+
+@Injectable()
+export class BasketService {
+
+  public itemsAdded$ = new EventEmitter<MDMItem[]>();
+  public itemsRemoved$ = new EventEmitter<MDMItem[]>();
+  readonly preferencePrefix = 'basket.nodes.';
+
+  items: MDMItem[] = [];
+
+  constructor(private _pref: PreferenceService) {
+  }
+
+  public add(item: MDMItem) {
+    let existingItem = this.items.find(i => i.equals(item));
+
+    if (!existingItem) {
+      this.items.push(item);
+      this.itemsAdded$.emit([item]);
+    }
+  }
+
+  public addAll(items: MDMItem[]) {
+    let newItemsWithoutExisting = items.filter(newItem => this.items.findIndex(existingItem => existingItem.equals(newItem)) < 0);
+
+    if (newItemsWithoutExisting) {
+      newItemsWithoutExisting.forEach(item => this.items.push(item));
+      this.itemsAdded$.emit(newItemsWithoutExisting);
+    }
+  }
+
+  public remove(item: MDMItem) {
+    let itemsToRemove = this.items.filter(i => i.equals(item));
+
+    if (itemsToRemove.length >= 0) {
+      itemsToRemove.forEach(i => this.items = this.items.filter(it => !it.equals(i)));
+      this.itemsRemoved$.emit(itemsToRemove);
+    }
+  }
+
+  removeAll() {
+    this.items = [];
+  }
+
+  saveBasketWithName(name: string) {
+    return this.saveBasket(new Basket(name, this.items));
+  }
+
+  saveBasket(basket: Basket) {
+    return this._pref.savePreference(this.basketToPreference(basket)).subscribe();
+  }
+
+  getBaskets() {
+    return this._pref.getPreference(this.preferencePrefix)
+      .map(preferences => preferences.map(p => this.preferenceToBasket(p)));
+  }
+
+  getItems() {
+    return this.items;
+  }
+
+  setItems(items: MDMItem[]) {
+    this.items = items;
+  }
+
+  private preferenceToBasket(pref: Preference) {
+    return deserialize(Basket, pref.value);
+  }
+
+  private basketToPreference(basket: Basket) {
+    const pref = new Preference();
+    pref.value = serialize(basket);
+    pref.key = this.preferencePrefix + basket.name;
+    pref.scope = Scope.USER;
+    return pref;
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.css b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.css
new file mode 100644
index 0000000..f6e1a85
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.css
@@ -0,0 +1,46 @@
+@charset "ISO-8859-1";

+

+/*******************************************************************************

+* Copyright (c) 2017 Peak Solution GmbH

+* All rights reserved. This program and the accompanying materials

+* are made available under the terms of the Eclipse Public License v1.0

+* which accompanies this distribution, and is available at

+* http://www.eclipse.org/legal/epl-v10.html

+*

+* Contributors:

+* Matthias Koller, Johannes Stamm - initial implementation

+*******************************************************************************/

+

+.remove {

+  color: black;

+  cursor: pointer;

+  float: right;

+}

+

+.fileupload {

+  overflow: hidden;

+  position: relative;

+}

+

+.fileupload input.upload {

+  float: right;

+  position: absolute;

+  top: 0;

+  right: 0;

+  margin: 0;

+  padding: 0;

+  font-size: 20px;

+  cursor: pointer;

+  opacity: 0;

+  filter: alpha(opacity=0);

+}

+

+a.disabled {

+  pointer-events: none;

+  cursor: not-allowed;

+}

+

+.badge{

+  margin: 0 0 2px 6px;

+  float: none;

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.html
new file mode 100644
index 0000000..200faf3
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.html
@@ -0,0 +1,133 @@
+<!--****************************************************************************

+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *

+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Dennis Schroeder - initial implementation                                   *

+*  Matthias Koller, Johannes Stamm - additional client functionality           *

+*****************************************************************************-->

+

+<div style="padding-top: 15px;">

+  <accordion>

+    <accordion-group #basketGroup [isOpen]="false">

+      <div accordion-heading class="thinheader">

+        <div class="row">

+          <div class="col-xs-7">

+            <div class="pull-left">

+              {{LblBasket}} &nbsp;&nbsp;

+              <mdm-view (click)="onViewClick($event)"></mdm-view>

+            </div>

+          </div>

+          <div class="col-xs-5 pull-right">

+            <div class="pull-right">

+              <button type="submit" class="btn btn-default" (click)="clearBasket($event)" title="{{TtlClearShoppingBasket}}"><span class="glyphicon glyphicon-erase"></span></button>

+              <button type="submit" class="btn btn-default" (click)="downloadBasket($event)" title="{{TtlDownloadShoppingBasket}}" [disabled]="isDownloadDisabled()"><span class="glyphicon glyphicon-download"></span></button>

+              <div class="fileupload btn btn-default" title="{{TtlUploadShoppingBasket}}" (click)=onUploadClick($event)>

+                <span class="glyphicon glyphicon-upload"></span>

+                <input title="{{TtlUploadShoppingBasket}}" class="upload" name="datei" type="file" accept=".json, application/json" id="fileInput" (change)="onUploadChange($event)">

+              </div>

+              <button type="submit" class="btn btn-default" (click)="showLoadModal($event)" title="{{TtlLoadShoppingBasket}}"><span class="glyphicon glyphicon-folder-open"></span></button>

+              <button type="submit" class="btn btn-default" (click)="showSaveModal($event)" title="{{TtlSaveShoppingBasket}}"><span class="fa fa-floppy-o"></span></button>

+              &nbsp;&nbsp;

+              <span class="glyphicon" [ngClass]="{'glyphicon-chevron-down': basketGroup?.isOpen, 'glyphicon-chevron-right': !basketGroup?.isOpen}"></span>

+            </div>

+          </div>

+        </div>

+      </div>

+      <div class="container-fluid">

+        <div class="row">

+            <mdm-tableview [results]="basketContent" [view]="viewComponent.selectedView" isRemovable="true" [menuItems]="contextMenuItems" [environments]="environments">

+            </mdm-tableview>

+        </div>

+      </div>

+    </accordion-group>

+  </accordion>

+</div>

+

+<div bsModal #lgLoadModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="SelectSearchComponents" aria-hidden="true" (keyup.enter)="loadBasket()">

+  <div class="modal-dialog modal-md">

+    <div class="modal-content">

+      <div class="modal-header">

+        <button type="button" title="{{TtlClose}}" class="close" (click)="childLoadModal.hide()" aria-label="Close">

+          <span aria-hidden="true">&times;</span>

+        </button>

+        <h4 class="modal-title">{{LblLoadBasket}}</h4>

+      </div>

+      <div class="modal-body">

+        <div class="container-fluid">

+          <div class="row" *ngIf="baskets.length > 0">

+            <ul class="list-group" style="max-height:80vh; padding: 0; list-style-type: none; overflow-y:auto;">

+              <div style="display: table; width: 100%; border: 1px solid #ddd;">

+                <li class="list-group-item" *ngFor="let basket of baskets" [ngClass]="{'active': selectedBasket == basket}" (click)="toggleSelect(basket)" (dblclick)="loadBasket(basket)" style="cursor: pointer">

+                  {{basket.name}}<span class="badge">{{basket.items.length}}</span>

+                </li>

+              </div>

+            </ul>

+          </div>

+          <div class="row" *ngIf="baskets.length === 0">

+            Keine gespeicherten Warenkörbe vorhanden.

+          </div>

+          <div class="row" style="margin-top: 20px;">

+            <button type="button" class="btn btn-default pull-right" (click)="loadBasket()" [disabled]="baskets.length <= 0 || selectedBasket == undefined">

+              <span class="glyphicon glyphicon-open"></span> {{LblLoad}}

+            </button>

+          </div>

+        </div>

+      </div>

+    </div>

+  </div>

+</div>

+

+<div bsModal #lgSaveModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="SelectSearchComponents" aria-hidden="true">

+  <div class="modal-dialog modal-md">

+    <div class="modal-content">

+      <div class="modal-header">

+        <button type="button" title="{{TtlClose}}" class="close" (click)="childSaveModal.hide()" aria-label="Close">

+          <span aria-hidden="true">&times;</span>

+        </button>

+        <h4 class="modal-title">{{LblSaveBasketAs}}:</h4>

+      </div>

+      <div class="modal-body">

+        <div class="container-fluid">

+          <div class="row" *ngIf="baskets.length > 0">

+            <p-dataTable

+              [value]="baskets"

+              resizableColumns="false"

+              [reorderableColumns]="false"

+              [rows]="10"

+              [paginator]="true"

+              [pageLinks]="3"

+              [rowsPerPageOptions]="[10,20,50]"

+              [(selection)]="selectedRow"

+              (onRowClick)="onRowSelect($event)"

+              (onRowSelect)="onRowSelect($event)">

+              <p-column [style]="{'width':'30px'}" selectionMode="single"></p-column>

+              <p-column header="{{LblExistingBasketNames}}">

+                <template pTemplate="body" let-col let-row="rowData" >

+                  {{row.name}}

+                </template>

+              </p-column>

+            </p-dataTable>

+          </div>

+          <div class="row" style="margin-top: 15px;">

+            <div class="col-md-10" style="padding-left: 0;">

+              <input type="text" class="form-control" placeholder="Warenkorb-Name" [value]="basketName" (input)="basketName = $event.target.value" (keyup.enter)="saveBasket($event)" required>

+            </div>

+            <div class="col-md-2" style="padding: 0;">

+              <button type="button" class="btn btn-default pull-right" (click)="saveBasket($event)" [disabled]="!basketName" title="{{getSaveBtnTitle()}}">

+                <span class="fa fa-floppy-o"></span> {{LblSave}}

+              </button>

+            </div>

+          </div>

+        </div>

+      </div>

+    </div>

+  </div>

+</div>

+

+<overwrite-dialog></overwrite-dialog>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.ts
new file mode 100644
index 0000000..30be847
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.ts
@@ -0,0 +1,256 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import { Component, Input, Output, EventEmitter, OnInit, ViewChild } from '@angular/core';
+import { DomSanitizer } from '@angular/platform-browser';
+import { ModalDirective } from 'ng2-bootstrap';
+
+import { BasketService, Basket} from './basket.service';
+
+import { MDMItem} from '../core/mdm-item';
+import { OverwriteDialogComponent } from '../core/overwrite-dialog.component';
+import { NavigatorService } from '../navigator/navigator.service';
+import { Node} from '../navigator/node';
+import { NodeService } from '../navigator/node.service';
+import { TableviewComponent } from '../tableview/tableview.component';
+import { ViewComponent } from '../tableview/view.component';
+import { View } from '../tableview/tableview.service';
+import { QueryService, Query, SearchResult, Row, Filter } from '../tableview/query.service';
+
+import { serialize, deserialize } from 'class-transformer';
+
+import {MenuItem} from 'primeng/primeng';
+import * as FileSaver from 'file-saver';
+
+@Component({
+  selector: 'mdm-basket',
+  templateUrl: 'mdm-basket.component.html',
+  styleUrls: ['./mdm-basket.component.css'],
+  providers: []
+})
+export class MDMBasketComponent implements OnInit {
+  @Output() onActive = new EventEmitter<Node>();
+  @Output() onSelect = new EventEmitter<Node>();
+  @Input() activeNode: Node;
+
+  readonly LblBasket = 'Warenkorb';
+  readonly LblExistingBasketNames = 'Vorhandene Warenkörbe';
+  readonly LblLoad = 'Laden';
+  readonly LblLoadBasket = 'Warenkorb öffnen';
+  readonly LblSave = 'Speichern';
+  readonly LblSaveBasketAs = 'Warenkorb speichern als';
+  readonly TtlClearShoppingBasket = 'Warenkorb leeren';
+  readonly TtlClose = 'Schließen';
+  readonly TtlDownloadShoppingBasket = 'Warenkorb herunterladen';
+  readonly TtlLoadShoppingBasket = 'Warenkorb öffnen';
+  readonly TtlNoNameSet = 'Kein Name ausgewählt';
+  readonly TtlSaveShoppingBasket = 'Warenkorb speichern';
+  readonly TtlUploadShoppingBasket = 'Warenkorb hochladen';
+
+  basketName = '';
+  basketContent: SearchResult = new SearchResult();
+  basket = 'Warenkorb';
+
+  baskets: Basket[] = [];
+  selectedBasket: Basket;
+  environments: Node[];
+
+  public selectedRow: string;
+  public lazySelectedRow: string;
+
+  _currentChange: any;
+
+  contextMenuItems: MenuItem[] = [
+      {label: 'Selektion aus Warenkorb entfernen', icon: 'glyphicon glyphicon-remove', command: (event) => this.removeSelected() }
+  ];
+
+  @ViewChild(TableviewComponent)
+  tableViewComponent: TableviewComponent;
+  @ViewChild(ViewComponent)
+  viewComponent: ViewComponent;
+  @ViewChild('lgLoadModal')
+  childLoadModal: ModalDirective;
+  @ViewChild('lgSaveModal')
+  childSaveModal: ModalDirective;
+  @ViewChild(OverwriteDialogComponent)
+  private overwriteDialogComponent: OverwriteDialogComponent;
+
+  constructor(private _basketService: BasketService,
+              private queryService: QueryService,
+              private navigatorService: NavigatorService,
+              private sanitizer: DomSanitizer,
+              private NodeService: NodeService) {
+  }
+
+  removeSelected() {
+    this.tableViewComponent.selectedRows.map(r => r.getItem()).forEach(i => this._basketService.remove(i));
+  }
+
+  ngOnInit() {
+    this.NodeService.getRootNodes().subscribe(envs => this.environments = envs);
+    this.setItems(this._basketService.items);
+    this._basketService.itemsAdded$.subscribe(items => this.addItems(items));
+    this._basketService.itemsRemoved$.subscribe(items => this.removeItems(items));
+    this.viewComponent.viewChanged$.subscribe(() => this.setItems(this._basketService.items));
+  }
+
+  onViewClick(e: Event) {
+    e.stopPropagation();
+  }
+
+  setItems(items: MDMItem[]) {
+    this.basketContent.rows = [];
+    this.addItems(items);
+  }
+
+  addItems(items: MDMItem[]) {
+    if (this.viewComponent.selectedView) {
+      this.queryService.queryItems(items, this.viewComponent.selectedView.columns.map(c => c.type + '.' + c.name))
+        .forEach(q => q.subscribe(r => this.addData(r.rows)));
+    }
+  }
+
+  removeItems(items: MDMItem[]) {
+    items.forEach(item =>
+      this.basketContent.rows = this.basketContent.rows.filter(row =>
+        !(row.source === item.source && row.type === item.type && +row.id === item.id)));
+  }
+
+  setView(view: View) {
+    console.log('setView', view);
+  }
+
+  saveBasket(e: Event) {
+    e.stopPropagation();
+    if (this.baskets.find(f => f.name === this.basketName) !== undefined) {
+      this.childSaveModal.hide();
+      this.overwriteDialogComponent.showOverwriteModal('ein Warenkorb').subscribe(ovw => this.saveBasket2(ovw));
+    } else {
+      this.saveBasket2(true);
+    }
+  }
+
+  saveBasket2(save: boolean) {
+    if (save) {
+      this._basketService.saveBasketWithName(this.basketName);
+      this.childSaveModal.hide();
+    } else {
+      this.childSaveModal.show();
+    }
+  }
+
+  loadBasket(basket?: Basket) {
+    if (basket === undefined) {
+      basket = this.selectedBasket;
+      if (this.selectedBasket === undefined) {
+        return;
+      }
+    }
+    this.basketName = basket.name;
+    this.setItems(basket.items);
+    this._basketService.setItems(basket.items);
+    this.childLoadModal.hide();
+  }
+
+  loadBaskets() {
+    this._basketService.getBaskets().subscribe(baskets => this.baskets = baskets);
+  }
+
+  clearBasket(e: Event) {
+    e.stopPropagation();
+    this.basketContent = new SearchResult();
+    this._basketService.removeAll();
+    this.basketName = '';
+  }
+
+  showLoadModal(e: Event) {
+    e.stopPropagation();
+    this.selectedBasket = undefined;
+    this.loadBaskets();
+    this.childLoadModal.show();
+  }
+
+  showSaveModal(e: Event) {
+    e.stopPropagation();
+    this.loadBaskets();
+    this.basketName = this.selectedBasket ? this.selectedBasket.name : '';
+    this.childSaveModal.show();
+  }
+
+  downloadBasket(e: Event) {
+    e.stopPropagation();
+    let downloadContent = new Basket(this.basketName, this._basketService.getItems());
+    let blob = new Blob([serialize(downloadContent)], {
+         type: 'application/json'
+     });
+    if (this.basketName && this.basketName.trim().length !== 0) {
+      FileSaver.saveAs(blob, this.basketName + '.json');
+    } else {
+      FileSaver.saveAs(blob, 'warenkorb.json');
+    }
+  }
+
+  onUploadChange(event: Event) {
+    this._currentChange = event.target;
+    this.onUploadEvent(this._currentChange);
+  }
+
+  onUploadClick(e: Event) {
+    e.stopPropagation();
+  }
+
+  toggleSelect(basket: Basket) {
+    this.selectedBasket = this.selectedBasket === basket ? undefined : basket;
+  }
+
+  isDownloadDisabled() {
+    return this.basketContent.rows.length <= 0;
+  }
+
+  getSaveBtnTitle() {
+    return this.basketName ? this.TtlSaveShoppingBasket : this.TtlNoNameSet;
+  }
+
+  private onUploadEvent(fileInput: any) {
+    if (fileInput.files[0]) {
+      let file = fileInput.files[0];
+      let reader = new FileReader();
+      reader.onloadend = (event) => {
+        let upload: Basket = deserialize(Basket, reader.result);
+        this.loadBasket(upload);
+        fileInput.value = '';
+    };
+    reader.readAsText(file);
+  }
+  }
+
+  private addData(rows: Row[]) {
+    rows.forEach(row => this.basketContent.rows.push(row));
+    this.tableViewComponent.customSort({
+      'field': this.tableViewComponent.view.getSortField(),
+      'order': this.tableViewComponent.view.getSortOrder()
+    });
+  }
+
+  onRowSelect(e: any) {
+    if (this.lazySelectedRow !== e.data) {
+      this.selectedRow = e.data;
+      this.basketName = e.data.name;
+    } else {
+      this.selectedRow = undefined;
+      this.basketName = '';
+    }
+    this.lazySelectedRow = this.selectedRow;
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.module.ts
new file mode 100644
index 0000000..40bb5b1
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.module.ts
@@ -0,0 +1,25 @@
+// *****************************************************************************

+// * Copyright (c) 2017 Peak Solution GmbH

+// * All rights reserved. This program and the accompanying materials

+// * are made available under the terms of the Eclipse Public License v1.0

+// * which accompanies this distribution, and is available at

+// * http://www.eclipse.org/legal/epl-v10.html

+// *

+// * Contributors:

+// * Matthias Koller, Johannes Stamm - initial implementation

+// *****************************************************************************

+

+import { NgModule } from '@angular/core';

+import { MDMBasketComponent } from './mdm-basket.component';

+import { MDMCoreModule } from '../core/mdm-core.module';

+import { TableViewModule } from '../tableview/tableview.module';

+

+@NgModule({

+  imports: [

+    MDMCoreModule,

+    TableViewModule

+  ],

+  declarations: [ MDMBasketComponent ],

+  exports: [ MDMBasketComponent],

+})

+export class MDMBasketModule { }

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-core.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-core.module.ts
new file mode 100644
index 0000000..944345f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-core.module.ts
@@ -0,0 +1,74 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+import { CommonModule } from '@angular/common';

+import { HttpModule } from '@angular/http';

+import { FormsModule } from '@angular/forms';

+

+import { Ng2BootstrapModule } from 'ng2-bootstrap';

+import { TypeaheadModule } from 'ng2-bootstrap';

+import { DatepickerModule } from 'ng2-bootstrap';

+import { TabsModule } from 'ng2-bootstrap/tabs';

+import { PositioningService } from 'ng2-bootstrap/positioning';

+import { ComponentLoaderFactory } from 'ng2-bootstrap/component-loader';

+

+import { DropdownMultiselectModule } from 'ng2-dropdown-multiselect';

+import { TreeModule, DataTableModule, SharedModule, ContextMenuModule, GrowlModule } from 'primeng/primeng';

+import { PropertyService } from './property.service';

+import { PreferenceService } from './preference.service';

+

+import { MDMNotificationComponent } from './mdm-notification.component';

+import { OverwriteDialogComponent } from './overwrite-dialog.component';

+

+@NgModule({

+  imports: [

+    HttpModule,

+    FormsModule,

+    CommonModule,

+    Ng2BootstrapModule,

+    TabsModule.forRoot(),

+    TypeaheadModule.forRoot(),

+    DatepickerModule.forRoot(),

+    DropdownMultiselectModule,

+    TreeModule,

+    DataTableModule,

+    SharedModule,

+    ContextMenuModule,

+    GrowlModule

+  ],

+  declarations: [

+    MDMNotificationComponent,

+    OverwriteDialogComponent

+  ],

+  exports: [

+    CommonModule,

+    FormsModule,

+    Ng2BootstrapModule,

+    DropdownMultiselectModule,

+    TreeModule,

+    DataTableModule,

+    SharedModule,

+    ContextMenuModule,

+    GrowlModule,

+    MDMNotificationComponent,

+    OverwriteDialogComponent

+  ],

+  providers: [

+      PositioningService,

+      ComponentLoaderFactory,

+

+      PropertyService,

+      PreferenceService

+    ],

+})

+export class MDMCoreModule { }

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-item.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-item.ts
new file mode 100644
index 0000000..07239d8
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-item.ts
@@ -0,0 +1,33 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import {Node} from '../navigator/node';

+

+export class MDMItem {

+  source: string;

+  type: string;

+  id: number;

+

+  constructor(source: string, type: string, id: number) {

+    this.source = source;

+    this.type = type;

+    this.id = id;

+  }

+

+  equalsNode(node: Node) {

+    return this.source === node.sourceName && this.type === node.type && this.id === node.id;

+  }

+

+  equals(item: MDMItem) {

+    return this.source === item.source && this.type === item.type && this.id === item.id;

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-notification.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-notification.component.ts
new file mode 100644
index 0000000..140bb44
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-notification.component.ts
@@ -0,0 +1,43 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { Component, OnInit, OnDestroy } from '@angular/core';

+import { Message } from 'primeng/primeng';

+import { MDMNotificationService } from './mdm-notification.service';

+import { Subscription } from 'rxjs/Subscription';

+

+@Component({

+  selector: 'mdm-notifications',

+  template: '<p-growl [value]="msgs" sticky="true"></p-growl>'

+})

+export class MDMNotificationComponent implements OnInit, OnDestroy {

+  msgs: Message[] = [];

+  subscription: Subscription;

+

+  constructor(private notificationsService: MDMNotificationService) { }

+

+  ngOnInit() {

+    this.subscribeToNotifications();

+  }

+

+  subscribeToNotifications() {

+    this.subscription = this.notificationsService.notificationChange

+    .subscribe(notification => {

+      this.msgs.length = 0;

+      this.msgs.push(notification);

+    });

+  }

+

+  ngOnDestroy() {

+    this.subscription.unsubscribe();

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-notification.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-notification.service.ts
new file mode 100644
index 0000000..9e41834
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-notification.service.ts
@@ -0,0 +1,42 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { Injectable, EventEmitter } from '@angular/core';

+import { Message } from 'primeng/primeng';

+

+type Severities = 'success' | 'info' | 'warn' | 'error';

+

+@Injectable()

+export class MDMNotificationService {

+  notificationChange = new EventEmitter<Message>();

+

+  notify(severity: Severities, summary: string, detail: string) {

+    this.notificationChange.emit({ severity, summary, detail });

+    console.log(severity, summary, detail);

+  }

+

+  notifyError(summary: string, detail: string) {

+    this.notify('error', summary, detail);

+  }

+

+  notifyWarn(summary: string, detail: string) {

+    this.notify('warn', summary, detail );

+  }

+

+  notifyInfo(summary: string, detail: string) {

+    this.notify('info', summary, detail);

+  }

+

+  notifySuccess(summary: string, detail: string) {

+    this.notify('success', summary, detail);

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/core/overwrite-dialog.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/core/overwrite-dialog.component.html
new file mode 100644
index 0000000..83cd5e5
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/overwrite-dialog.component.html
@@ -0,0 +1,33 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<div bsModal #lgOverwriteModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="SelectSearchComponents" aria-hidden="true">

+  <div class="modal-dialog modal-sm">

+    <div class="modal-content">

+      <div class="modal-header">

+        <h4 class="modal-title">Es existiert bereits {{label}} mit diesem Namen!</h4>

+      </div>

+      <div class="modal-body">

+        <div class="row">

+          <div class="col-md-12">

+            <button type="button" class="btn btn-default" (click)="overwrite(false)">

+            <span class="glyphicon glyphicon-remove"></span> {{LblCancel}}

+          </button>

+            <button type="button" class="btn btn-default pull-right" (click)="overwrite(true)">

+            <span class="glyphicon glyphicon-ok"></span> {{LblOverwrite}}

+          </button>

+          </div>

+        </div>

+      </div>

+    </div>

+  </div>

+</div>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/core/overwrite-dialog.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/overwrite-dialog.component.ts
new file mode 100644
index 0000000..cfee036
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/overwrite-dialog.component.ts
@@ -0,0 +1,43 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { Component, ViewChild, EventEmitter} from '@angular/core';

+import { ModalDirective } from 'ng2-bootstrap';

+

+@Component({

+  selector: 'overwrite-dialog',

+  templateUrl: 'overwrite-dialog.component.html'

+})

+export class OverwriteDialogComponent {

+

+  readonly LblCancel = 'Abbrechen';

+  readonly LblOverwrite = 'Überschreiben';

+

+  label: string;

+  overwriteEvent = new EventEmitter<boolean>();

+

+  @ViewChild('lgOverwriteModal')

+  private childOverwriteModal: ModalDirective;

+

+  constructor () {}

+

+  showOverwriteModal(label: string) {

+    this.label = label;

+    this.childOverwriteModal.show();

+    return this.overwriteEvent;

+  }

+

+  overwrite(b: boolean) {

+    this.childOverwriteModal.hide();

+    this.overwriteEvent.emit(b);

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/core/preference.service.spec.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/preference.service.spec.ts
new file mode 100644
index 0000000..7214092
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/preference.service.spec.ts
@@ -0,0 +1,98 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { ComponentFixture, async, TestBed, inject } from '@angular/core/testing';

+import { BaseRequestOptions, Http, HttpModule, Response, ResponseOptions, RequestMethod } from '@angular/http';

+import { MockBackend } from '@angular/http/testing';

+

+import {PreferenceService, Preference, Scope} from './preference.service';

+import {PropertyService} from './property.service';

+

+describe('PreferenceService', () => {

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        PropertyService,

+        PreferenceService,

+        MockBackend,

+        BaseRequestOptions,

+        {

+          provide: Http,

+          useFactory: (mockBackend, options) => {

+            return new Http(mockBackend, options);

+          },

+          deps: [MockBackend, BaseRequestOptions]

+        }

+      ]

+    });

+  });

+

+

+  describe('getPreference()', () => {

+    it('should return preferences', async(inject([PreferenceService, MockBackend], (prefService, mockBackend) => {

+

+      mockBackend.connections.subscribe(conn => {

+

+        let mockResponse = {

+          preferences: [

+          {

+            id: 2,

+            key: 'preference.prefix.',

+            scope: Scope.SYSTEM,

+            source: null,

+            user: null,

+            value: 'Test'

+          }

+        ]};

+        conn.mockRespond(new Response(new ResponseOptions({ body: mockResponse })));

+      });

+

+      prefService.getPreference(Scope.SYSTEM, 'preference.prefix.').subscribe(prefs => {

+        expect(prefs.length).toBe(1);

+        expect(prefs[0].scope).toBe(Scope.SYSTEM);

+        expect(prefs[0].value).toBe('Test');

+      });

+    })));

+

+    it('should return empty array if no preferences were found',

+        async(inject([PreferenceService, MockBackend], (prefService, mockBackend) => {

+

+      mockBackend.connections.subscribe(conn => {

+        conn.mockRespond(new Response(new ResponseOptions({ body: { preferences: [] } })));

+      });

+

+      prefService.getPreference(Scope.SYSTEM, 'preference.prefix.').subscribe(prefs => {

+        expect(prefs.length).toBe(0);

+      });

+    })));

+  });

+

+  describe('savePreference()', () => {

+    it('should post preference', async(inject([PreferenceService, MockBackend], (prefService, mockBackend) => {

+

+      mockBackend.connections.subscribe(conn => {

+        if (conn.request.url.endsWith('/preference') && conn.request.method === RequestMethod.Put) {

+          conn.mockRespond(new Response(new ResponseOptions({ body: { preferences: [] } })));

+        }

+      });

+      let newPref = new Preference();

+      newPref.scope = Scope.SYSTEM;

+      newPref.key = 'prefix.';

+      newPref.value = 'testValue';

+

+      prefService.savePreference(newPref).subscribe(prefs => {

+        expect(prefs).toBeDefined();

+      });

+    })));

+  });

+});

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/core/preference.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/preference.service.ts
new file mode 100644
index 0000000..1669908
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/preference.service.ts
@@ -0,0 +1,103 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { Injectable } from '@angular/core';

+import { Http, Response, Headers, RequestOptions } from '@angular/http';

+import { Observable } from 'rxjs/Observable';

+

+import { PropertyService } from './property.service';

+

+import {Type, Exclude, plainToClass, serialize, deserializeArray} from 'class-transformer';

+

+export class Scope {

+   public static readonly SYSTEM = 'SYSTEM';

+   public static readonly SOURCE = 'SOURCE';

+   public static readonly USER = 'USER';

+

+   static toLabel(scope: string) {

+     return scope.charAt(0).toUpperCase() + scope.slice(1).toLowerCase();

+   }

+}

+

+export class Preference {

+  scope: string;

+  source?: string;

+  user?: string;

+  key: string;

+  value: string;

+  id: number;

+

+  static sortByScope(p1: Preference, p2: Preference) {

+    let getPriority = (scope: string) => {

+      switch (scope) {

+        case Scope.SYSTEM: return 1;

+        case Scope.SOURCE: return 2;

+        case Scope.USER: return 3;

+        default: return 4;

+      }

+    };

+    return getPriority(p1.scope) - getPriority(p2.scope);

+  }

+

+  constructor() {

+      this.key = '';

+  }

+}

+

+@Injectable()

+export class PreferenceService {

+

+  private prefEndpoint: string;

+

+  constructor(private http: Http,

+              private _prop: PropertyService) {

+    this.prefEndpoint = _prop.getUrl('/mdm/preferences');

+  }

+

+  getPreferenceForScope(scope: string, key?: string) {

+      if (key == null) {

+          key = '';

+      }

+      return this.http.get(this.prefEndpoint + '?scope=' + scope + '&key=' + key)

+          .map(response => plainToClass(Preference, response.json().preferences));

+  }

+

+  getPreference(key?: string) {

+      if (key == null) {

+          key = '';

+      }

+      return this.http.get(this.prefEndpoint + '?key=' + key)

+          .map(response => plainToClass(Preference, response.json().preferences));

+  }

+  savePreference(preference: Preference) {

+    let headers = new Headers({ 'Content-Type': 'application/json' });

+    let options = new RequestOptions({ headers: headers });

+

+    return this.http.put(this.prefEndpoint, JSON.stringify(preference), options)

+      .catch(this.handleError);

+  }

+

+  deletePreference(id: number) {

+    return this.http.delete(this.prefEndpoint + '/' + id);

+  }

+

+  deletePreferenceByScopeAndKey(scope: string, key: string) {

+    return this.getPreferenceForScope(scope, key).flatMap(p => this.deletePreference(p[0].id));

+

+    // this.getPreferenceForScope(scope, key).subscribe( p => this.deletePreference(p[0].id).subscribe());

+  }

+

+  private handleError(error: Response) {

+      console.error(error);

+      return Observable.throw(error.json().error || 'Server error');

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/app/properties.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/property.service.ts
similarity index 65%
rename from org.eclipse.mdm.application/src/main/webapp/app/properties.ts
rename to org.eclipse.mdm.application/src/main/webapp/src/app/core/property.service.ts
index e21cecc..916f351 100644
--- a/org.eclipse.mdm.application/src/main/webapp/app/properties.ts
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/property.service.ts
@@ -7,11 +7,22 @@
 //   *
 //   * Contributors:
 //   * Dennis Schroeder - initial implementation
+//   * Matthias Koller, Johannes Stamm - additional client functionality
 //   *******************************************************************************
 import {Injectable} from '@angular/core';
 
 @Injectable()
 export class PropertyService {
-  api_host: string = '';
-  data_host: string ='http://localhost:8080/';
+  api_host = '/org.eclipse.mdm.nucleus';
+  data_host = 'http://sa:sa@localhost:9090/';
+
+  getDataHost(): string {
+      return this.data_host;
+  }
+
+  getUrl(restUrl: string): string {
+    if (this.api_host) {
+      return this.api_host + restUrl;
+    }
+  }
 }
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.service.ts
new file mode 100644
index 0000000..8b3c6ec
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.service.ts
@@ -0,0 +1,174 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Injectable} from '@angular/core';
+import {Http, Response, Headers, RequestOptions} from '@angular/http';
+import {Observable} from 'rxjs/Observable';
+import {Context, Sensor} from './context';
+import {PropertyService} from '../core/property.service';
+
+import {Node} from '../navigator/node';
+
+import {Components} from './context';
+
+@Injectable()
+export class ContextService {
+
+  private _contextUrl: string;
+
+  private test: {};
+  private errorMessage: string;
+
+  constructor(private http: Http,
+              private _prop: PropertyService) {
+    this._contextUrl = _prop.getUrl('/mdm/environments');
+  }
+
+  getContext(node: Node) {
+    let url = this._contextUrl + '/' + node.sourceName;
+    url = url + '/' + node.type.toLowerCase() + 's/' + node.id + '/contexts';
+    return this.get(url);
+  }
+
+  getSensors(node: Node) {
+    let url = this._contextUrl + '/' + node.sourceName + '/' + node.type.toLowerCase() + 's/' + node.id + '/contexts/testequipment/sensors';
+    return this.http.get(url)
+        .map((res) => { return <{}> this.merge(res.json().data); })
+        .catch(this.handleError);
+  }
+
+  private get(url: string) {
+    return this.http.get(url)
+    .map((res) => {
+      let data = res.json().data;
+      let context = this.specialMerger([data[0].contextOrdered, data[0].contextMeasured]);
+      return <{}> context;
+    })
+    .catch(this.handleError);
+  }
+
+  private merge(sensor: Sensor) {
+    let sensorm = sensor[0].sensorContextMeasured;
+    let sensoro = sensor[0].sensorContextOrdered;
+    let merge = [];
+    sensoro.forEach((node) => {
+      let pos = sensorm.map(function(e) { return e.name; }).indexOf(node.name);
+      if (pos === -1) {
+        merge.push(this.empty_m(node));
+      } else {
+        merge.push(this.mergeNode(node, sensorm[pos]));
+        sensorm.splice(pos, 1);
+      }
+    });
+    sensorm.forEach((node) => {
+      merge.push(this.empty_o(node));
+    });
+    return merge;
+  }
+
+  private mergeNode(oNode, mNode) {
+    oNode.attributes.forEach((attr, i) => {
+      attr.dataType = [attr.dataType, mNode.attributes[i].dataType];
+      attr.name = [attr.name, mNode.attributes[i].name];
+      attr.unit = [attr.unit, mNode.attributes[i].unit];
+      attr.value = [attr.value, mNode.attributes[i].value];
+    });
+    return oNode;
+  }
+
+  private empty_o(node) {
+    node.attributes.forEach((attr) => {
+      attr.dataType = ['', attr.dataType];
+      attr.name = ['', attr.name];
+      attr.unit = ['', attr.unit];
+      attr.value = ['', attr.unit];
+    });
+    return node;
+  }
+
+  private empty_m(node) {
+    node.attributes.forEach((attr) => {
+      attr.dataType = [attr.dataType, ''];
+      attr.name = [attr.name, ''];
+      attr.unit = [attr.unit, ''];
+      attr.value = [attr.unit, ''];
+    });
+    return node;
+  }
+
+  private specialMerger(contexts) {
+    let result = new Object();
+    let resultattributegroup, resultattributes, resultattribute;
+
+    for (let i = 0; i < contexts.length; ++i) {
+      for (let testname in contexts[i]) {
+        if (contexts[i].hasOwnProperty(testname)) {
+          let test = contexts[i][testname];
+          if (!Array.isArray(test)) { continue; }
+
+          if (!result[testname]) {
+            result[testname] = new Array();
+          }
+
+          let subresult = result[testname];
+
+          for (let j = 0; j < test.length; ++j) {
+            let attributegroup = test[j];
+            if (!(attributegroup instanceof Object)) { continue; }
+
+            let index = -1;
+            subresult.forEach(function (_resultattributegroup, idx) {
+              if (_resultattributegroup['name'] === attributegroup['name']) { index = idx; return true; }
+            });
+            if (index < 0) {
+              index = subresult.length;
+              subresult.push(resultattributegroup = JSON.parse(JSON.stringify(attributegroup)));
+              resultattributegroup['attributes'] = new Array();
+            } else {
+              resultattributegroup = subresult[index];
+            }
+            resultattributes = resultattributegroup['attributes'];
+
+            let attributes = attributegroup['attributes'];
+            if (!Array.isArray(attributes)) { continue; };
+
+            for (let k = 0; k < attributes.length; ++k) {
+              let attribute = attributes[k];
+              if (!(attribute instanceof Object)) { continue; }
+
+              let index2 = -1;
+              resultattributes.forEach(function (_resultattribute, idx) {
+                if (_resultattribute['name'] === attribute['name']) { index2 = idx; return true; }
+              });
+              if (index2 < 0) {
+                index2 = resultattributes.length;
+                resultattributes.push(resultattribute = JSON.parse(JSON.stringify(attribute)));
+                resultattribute['value'] = new Array(contexts.length);
+              } else {
+                resultattribute = resultattributes[index2];
+              }
+              resultattribute['value'][i] = attribute['value'];
+            }
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  private handleError(error: Response) {
+    console.error(error);
+    return Observable.throw(error.json().error || 'Server error');
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.ts
new file mode 100644
index 0000000..e6ee2f7
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.ts
@@ -0,0 +1,31 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Node} from '../navigator/node';
+
+export class Context {
+  contextMeasured: Components;
+  contextOrdered: Components;
+}
+
+export class Components {
+  UNITUNDERTEST: Node[];
+  TESTSEQUENCE: Node[];
+  TESTEQUIPMENT: Node[];
+}
+
+export class Sensor {
+  sensorContextMeasured: Node[];
+  sensorContextOrdered: Node[];
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/detail-view.service.spec.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/detail-view.service.spec.ts
new file mode 100644
index 0000000..2e263ca
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/detail-view.service.spec.ts
@@ -0,0 +1,82 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { ComponentFixture, async, TestBed, inject } from '@angular/core/testing';

+import { BaseRequestOptions, Http, HttpModule, Response, ResponseOptions, RequestMethod } from '@angular/http';

+import { MockBackend } from '@angular/http/testing';

+import { Observable } from 'rxjs/Observable';

+

+import {PreferenceService, Preference, Scope} from '../core/preference.service';

+import {PropertyService} from '../core/property.service';

+import {DetailViewService} from './detail-view.service';

+import {MDMNotificationService} from '../core/mdm-notification.service';

+

+class TestPreferenceService {

+  getPreference(key?: string): Observable<Preference[]> {

+    return Observable.of([

+    {

+      id: 1,

+      key: 'ignoredAttributes',

+      scope: Scope.USER,

+      source: null,

+      user: 'testUser',

+      value: '[\"*.MimeType\", \"TestStep.Sortindex\"]'

+    }, {

+      id: 2,

+      key: 'ignoredAttributes',

+      scope: Scope.SYSTEM,

+      source: null,

+      user: null,

+      value: '[\"Project.*\"]'

+    }, {

+      id: 3,

+      key: 'ignoredAttributes',

+      scope: Scope.SOURCE,

+      source: 'MDMTEST',

+      user: null,

+      value: '[\"*.Id\"]'

+    }, {

+      id: 4,

+      key: 'ignoredAttributes',

+      scope: Scope.SOURCE,

+      source: 'MDM_OTHER',

+      user: null,

+      value: '[\"Pool.*\"]'

+    }

+  ]);

+  }

+}

+

+describe('DetailViewService', () => {

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        {

+          provide: PreferenceService,

+          useClass: TestPreferenceService

+        },

+        DetailViewService,

+        MDMNotificationService

+      ]

+    });

+  });

+

+

+  describe('getFilters()', () => {

+    it('should return filtered attributes', async(inject([DetailViewService], (detailViewService) => {

+

+      expect(detailViewService.getFilters('MDMTEST')).toEqual(['Project.*', '*.Id', '*.MimeType', 'TestStep.Sortindex']);

+

+    })));

+  });

+});

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/detail-view.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/detail-view.service.ts
new file mode 100644
index 0000000..38c9336
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/detail-view.service.ts
@@ -0,0 +1,72 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { Injectable} from '@angular/core';

+import { Preference, PreferenceService, Scope } from '../core/preference.service';

+import { MDMNotificationService } from '../core/mdm-notification.service';

+

+import { Node, Attribute } from '../navigator/node';

+

+@Injectable()

+export class DetailViewService {

+

+    ignoreAttributesPrefs: Preference[] = [];

+

+    constructor (private preferenceService: PreferenceService,

+                 private notificationService: MDMNotificationService) {

+      this.preferenceService.getPreference('ignoredAttributes')

+          .subscribe( prefs => this.ignoreAttributesPrefs = this.ignoreAttributesPrefs.concat(prefs));

+    }

+

+    getAttributesToDisplay(node: Node) {

+        let filterList = this.getFilters(node.sourceName)

+          .map(p => { let splitted = p.split('.'); return { type: splitted[0], attribute: splitted[1]}; })

+          .filter(p => p.type === node.type || p.type === '*')

+          .map(p => p.attribute);

+

+        return this.getFilteredAttributes(node.attributes, filterList);

+    }

+

+    getFilters(source: string): string[] {

+      if (this.ignoreAttributesPrefs.length > 0) {

+      return this.ignoreAttributesPrefs

+        .filter(p => p.scope !== Scope.SOURCE || p.source === source)

+        .sort(Preference.sortByScope)

+        .map(p => this.parsePreference(p))

+        .reduce((acc, value) => acc.concat(value), []);

+      } else {

+        return [];

+      }

+    }

+

+    private parsePreference(pref: Preference) {

+      try {

+          return <string[]> JSON.parse(pref.value);

+      } catch (e) {

+          this.notificationService.notifyError('Einstellungen für die zu ignorierenden Attribute ist fehlerhaft.', e);

+          return [];

+      }

+    }

+

+    private processFilter(prefList: string[], type: string) {

+      return prefList.filter(p => p.split('.')[0] === type || p.split('.')[0] === '*')

+        .map(p => p.split('.')[1]);

+    }

+

+    private getFilteredAttributes(attributes: Attribute[], filter: string[]) {

+        if (filter.indexOf('*') !== -1) {

+            return [];

+        } else {

+            return attributes.filter(attr => filter.indexOf(attr.name ) === -1);

+        }

+    }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-descriptive-data.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-descriptive-data.component.html
new file mode 100644
index 0000000..a7150fb
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-descriptive-data.component.html
@@ -0,0 +1,91 @@
+<!--****************************************************************************

+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *

+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Dennis Schroeder - initial implementation                                   *

+*  Matthias Koller, Johannes Stamm - additional client functionality           *

+*****************************************************************************-->

+

+<div *ngIf="!contexts">

+  <div class="alert alert-info" style="margin: 0;">

+    <strong>{{status}}</strong>

+  </div>

+</div>

+
+<div *ngIf="contexts">

+<accordion *ngIf="isUUT()">

+  <accordion-group *ngFor="let template of contexts['UNITUNDERTEST']" #UUT>

+    <div accordion-heading class="thinheader">{{template.name}}

+      <span class="pull-right glyphicon" [ngClass]="{'glyphicon-chevron-down': UUT?.isOpen, 'glyphicon-chevron-right': !UUT?.isOpen}"></span>

+    </div>

+    <table class="table table-hover">

+      <thead>

+        <tr>

+          <th>{{LblName}}</th>

+          <th>{{LblOrdered}}</th>

+          <th>{{LblMeasured}}</th>

+        </tr>

+      </thead>

+      <tbody>

+        <tr *ngFor="let attr of template.attributes" [ngClass]="diff(attr.value[0], attr.value[1])">

+          <td>{{attr.name}}</td>

+          <td>{{attr.value[0]}}</td>

+          <td>{{attr.value[1]}}</td>

+        </tr>

+      </tbody>

+    </table>

+  </accordion-group>

+</accordion>

+<accordion *ngIf="isTS()">

+  <accordion-group *ngFor="let template of contexts['TESTSEQUENCE']" #TS>

+    <div accordion-heading class="thinheader">{{template.name}}

+      <span class="pull-right glyphicon" [ngClass]="{'glyphicon-chevron-down': TS?.isOpen, 'glyphicon-chevron-right': !TS?.isOpen}"></span>

+    </div>

+    <table class="table table-hover">

+      <thead>

+        <tr>

+          <th>{{LblName}}</th>

+          <th>{{LblOrdered}}</th>

+          <th>{{LblMeasured}}</th>

+        </tr>

+      </thead>

+      <tbody>

+        <tr *ngFor="let attr of template.attributes" [ngClass]="diff(attr.value[0], attr.value[1])">

+          <td>{{attr.name}}</td>

+          <td>{{attr.value[0]}}</td>

+          <td>{{attr.value[1]}}</td>

+        </tr>

+      </tbody>

+    </table>

+  </accordion-group>

+</accordion>

+<accordion *ngIf="isTE()">

+  <accordion-group *ngFor="let template of contexts['TESTEQUIPMENT']" #TE>

+    <div accordion-heading class="thinheader">{{template.name}}

+      <span class="pull-right glyphicon" [ngClass]="{'glyphicon-chevron-down': TE?.isOpen, 'glyphicon-chevron-right': !TE?.isOpen}"></span>

+    </div>

+    <table class="table table-hover">

+      <thead>

+        <tr>

+          <th>{{LblName}}</th>

+          <th>{{LblOrdered}}</th>

+          <th>{{LblMeasured}}</th>

+        </tr>

+      </thead>

+      <tbody>

+        <tr *ngFor="let attr of template.attributes" [ngClass]="diff(attr.value[0], attr.value[1])">

+          <td>{{attr.name}}</td>

+          <td>{{attr.value[0]}}</td>

+          <td>{{attr.value[1]}}</td>

+        </tr>

+      </tbody>

+    </table>

+  </accordion-group>

+</accordion>

+</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-descriptive-data.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-descriptive-data.component.ts
new file mode 100644
index 0000000..32363f1
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-descriptive-data.component.ts
@@ -0,0 +1,126 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Component, OnInit, Input, OnChanges, SimpleChange} from '@angular/core';
+import { Router, ActivatedRoute, Params } from '@angular/router';
+
+import { AccordionComponent, AccordionModule } from 'ng2-bootstrap';
+import {LocalizationService} from '../localization/localization.service';
+
+import {NodeService} from '../navigator/node.service';
+import {ContextService} from './context.service';
+import {Context, Sensor} from './context';
+import {Node} from '../navigator/node';
+import {NavigatorService} from '../navigator/navigator.service';
+
+@Component({
+  selector: 'mdm-detail-context',
+  templateUrl: 'mdm-detail-descriptive-data.component.html',
+})
+
+export class MDMDescriptiveDataComponent implements OnInit {
+
+  readonly LblMeasured = 'Gemessen';
+  readonly LblName = 'Name';
+  readonly LblOrdered = 'Beauftragt';
+
+  readonly StatusLoading = 'Lädt..';
+  readonly StatusNoNodes = 'Keine Knoten verfügbar.';
+  readonly StatusNoDescriptiveData = 'Keine beschreibenden Daten verfügbar.';
+
+  selectedNode: Node;
+  context: String;
+
+  _diff = false;
+  contexts: Context[];
+  sensors: Sensor[];
+  errorMessage: string;
+  status: string = this.StatusLoading;
+
+  uut = 'Prüfling';
+  ts = 'Testablauf';
+  te = 'Messgerät';
+  s = 'Sensoren';
+
+  constructor(private route: ActivatedRoute,
+    private localService: LocalizationService,
+              private _contextService: ContextService,
+              private navigatorService: NavigatorService) {}
+
+  ngOnInit() {
+    this.status = this.StatusLoading;
+    this.route.params
+        .subscribe(params => this.setContext(params['context'])
+    );
+
+    this.navigatorService.selectedNodeChanged
+        .subscribe(node => this.loadContext(node));
+  }
+
+  setContext(context: string) {
+    this.context = context;
+    this.loadContext(this.navigatorService.getSelectedNode());
+  }
+
+  loadContext(node: Node) {
+    if (node) {
+      this.selectedNode = node;
+      this.contexts = undefined;
+      if (node.name !== undefined && (node.type.toLowerCase() === 'measurement' || node.type.toLowerCase() === 'teststep')) {
+        this.status = this.StatusLoading;
+        this._contextService.getContext(node).subscribe(
+          contexts => {
+            if (contexts.hasOwnProperty('UNITUNDERTEST')
+                || contexts.hasOwnProperty('TESTEQUIPMENT')
+                || contexts.hasOwnProperty('TESTSEQUENCE')) {
+                  this.contexts = contexts;
+                } else {
+                  this.status = this.StatusNoDescriptiveData;
+                }
+          },
+          error => this.errorMessage = <any>error);
+      } else {
+        this.status = this.StatusNoDescriptiveData;
+      }
+    } else {
+      this.status = this.StatusNoNodes;
+    }
+  }
+
+  diffToggle() {
+    this._diff = !this._diff;
+  }
+
+  diff(attr1: string, attr2: string) {
+    if (attr1 !== attr2 && this._diff) {
+      return 'danger';
+    }
+  }
+
+  isUUT() {
+    return this.context.toLowerCase() === 'uut';
+  }
+
+  isTE() {
+    return this.context.toLowerCase() === 'te';
+  }
+
+  isTS() {
+    return this.context.toLowerCase() === 'ts';
+  }
+
+  getTrans(type: string, attr: string) {
+    return this.localService.getTranslation(type, attr);
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-routing.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-routing.module.ts
new file mode 100644
index 0000000..059513f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-routing.module.ts
@@ -0,0 +1,38 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+import { RouterModule, Routes } from '@angular/router';

+

+import { MDMDetailComponent } from './mdm-detail.component';

+import { MDMDetailViewComponent } from './mdm-detail-view.component';

+import { MDMDescriptiveDataComponent } from './mdm-detail-descriptive-data.component';

+import { SensorComponent } from './sensor.component';

+

+const detailRoutes: Routes = [

+  { path: '',  component: MDMDetailComponent, children: [

+    { path: '', redirectTo: 'general', pathMatch: 'full' },

+    { path: 'general',  component: MDMDetailViewComponent },

+    { path: 'sensors',  component: SensorComponent },

+    { path: ':context', component: MDMDescriptiveDataComponent }

+  ]}

+];

+

+@NgModule({

+  imports: [

+    RouterModule.forChild(detailRoutes)

+  ],

+  exports: [

+    RouterModule

+  ]

+})

+export class MDMDetailRoutingModule {}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-view.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-view.component.html
new file mode 100644
index 0000000..5c2c6d0
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-view.component.html
@@ -0,0 +1,39 @@
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
+
+<div>
+  <div class="btn-group pull-right" role="group">
+    <button type="button" class="btn btn-default" (click)="add2Basket()" [disabled]="isShopable()">{{LblToBasket}}</button>
+    <!-- <mdm-filerelease-create [node]=selectedNode [disabled]="isReleasable()"></mdm-filerelease-create> -->
+  </div>
+  <h3 *ngIf="selectedNode" style="margin: 5px;" class="icon" [ngClass]="selectedNode.getClass()">
+    {{getTrans(selectedNode.type)}}
+  </h3>
+</div>
+<table class="table table-hover" style="margin-bottom: 0;">
+  <thead>
+    <tr>
+      <th>{{LblAttribute}}</th>
+      <th>{{LblValue}}</th>
+      <th>{{LblUnit}}</th>
+    </tr>
+  </thead>
+  <tbody *ngIf="selectedNode">
+    <tr *ngFor="let attr of displayAttributes">
+      <td>{{getTrans(selectedNode.type, attr.name)}}</td>
+      <td>{{attr.value}}</td>
+      <td>{{attr.unit}}</td>
+    </tr>
+  </tbody>
+</table>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-view.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-view.component.ts
new file mode 100644
index 0000000..eb95b9b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-view.component.ts
@@ -0,0 +1,91 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Component, OnInit, OnDestroy} from '@angular/core';
+
+import {MDMItem} from '../core/mdm-item';
+import {Node, Attribute} from '../navigator/node';
+import {BasketService} from '../basket/basket.service';
+
+import {Release, FilereleaseService} from '../filerelease/filerelease.service';
+
+import {Localization} from '../localization/localization';
+import {LocalizationService} from '../localization/localization.service';
+
+// import {MDMFilereleaseCreateComponent} from '../filerelease/mdm-filerelease-create.component';
+import {NavigatorService} from '../navigator/navigator.service';
+
+import { DetailViewService } from './detail-view.service';
+
+@Component({
+  selector: 'mdm-detail-view',
+  templateUrl: 'mdm-detail-view.component.html'
+})
+export class MDMDetailViewComponent implements OnInit, OnDestroy {
+
+  readonly LblAttribute = 'Attribut';
+  readonly LblToBasket = 'In den Warenkorb';
+  readonly LblUnit = 'Einheit';
+  readonly LblValue = 'Wert';
+
+  selectedNode: Node;
+  displayAttributes: Attribute[];
+
+  locals: Localization[] = [];
+  subscription: any;
+
+  constructor(private localService: LocalizationService,
+              private basketService: BasketService,
+              private navigatorService: NavigatorService,
+              private detailViewService: DetailViewService) {}
+
+  ngOnInit() {
+    this.onSelectedNodeChange(this.navigatorService.getSelectedNode());
+    this.subscription = this.navigatorService.selectedNodeChanged
+        .subscribe(node => this.onSelectedNodeChange(node));
+  }
+
+  ngOnDestroy() {
+    this.subscription.unsubscribe();
+  }
+
+  onSelectedNodeChange(node: Node) {
+    if (node) {
+      this.selectedNode = node;
+      this.displayAttributes = this.detailViewService.getAttributesToDisplay(this.selectedNode);
+    }
+  }
+
+  add2Basket() {
+    if (this.selectedNode) {
+      this.basketService.add(new MDMItem(this.selectedNode.sourceName, this.selectedNode.type, this.selectedNode.id));
+    }
+  }
+
+  isShopable() {
+    if (this.selectedNode && this.selectedNode.name !== undefined && this.selectedNode.type !== 'Environment') {
+      return false;
+    }
+    return true;
+  }
+
+  isReleasable() {
+    if (this.selectedNode && this.selectedNode.sourceType === 'TestStep') { return false; }
+    return true;
+  }
+
+  getTrans(type: string, attr: string) {
+    return this.localService.getTranslation(type, attr);
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.component.html
new file mode 100644
index 0000000..46e3f40
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.component.html
@@ -0,0 +1,29 @@
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
+
+<nav class="navbar navbar-default">
+  <div class="container-fluid">
+    <div class="collapse navbar-collapse" id="bs-mdm-detail-navbar">
+      <ul class="nav navbar-nav">
+        <li [routerLinkActive]="['active']"><a routerLink="general" style="cursor:pointer;"> {{LblGeneral}}</a></li>
+        <li [routerLinkActive]="['active']"><a routerLink="uut" style="cursor:pointer;"> {{uut}}</a></li>
+        <li [routerLinkActive]="['active']"><a routerLink="ts" style="cursor:pointer;"> {{ts}}</a></li>
+        <li [routerLinkActive]="['active']"><a routerLink="te" style="cursor:pointer;"> {{te}}</a></li>
+        <li [routerLinkActive]="['active']"><a routerLink="sensors" style="cursor:pointer;"> {{LblSensors}} </a></li>
+      </ul>
+    </div>
+  </div>
+</nav>
+
+<router-outlet></router-outlet>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.component.ts
new file mode 100644
index 0000000..ce8fbfe
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.component.ts
@@ -0,0 +1,36 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Component} from '@angular/core';
+
+import {MDMDetailViewComponent} from './mdm-detail-view.component';
+import {MDMDescriptiveDataComponent} from './mdm-detail-descriptive-data.component';
+import {SensorComponent} from './sensor.component';
+
+@Component({
+  selector: 'mdm-detail',
+  templateUrl: 'mdm-detail.component.html',
+  providers: []
+})
+export class MDMDetailComponent {
+
+  readonly uut = 'Prüfling';
+  readonly ts = 'Testablauf';
+  readonly te = 'Messgerät';
+  readonly s = 'Sensoren';
+  readonly brand = 'Details';
+  readonly LblGeneral = 'General';
+  readonly LblSensors = 'Sensoren';
+
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.module.ts
new file mode 100644
index 0000000..b043a1a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.module.ts
@@ -0,0 +1,48 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+

+import { MDMDetailRoutingModule } from './mdm-detail-routing.module';

+

+import { MDMCoreModule } from '../core/mdm-core.module';

+// import { MDMFilereleaseModule } from '../filerelease/mdm-filerelease.module';

+

+import { MDMDetailComponent } from './mdm-detail.component';

+import { MDMDetailViewComponent } from './mdm-detail-view.component';

+import { MDMDescriptiveDataComponent } from './mdm-detail-descriptive-data.component';

+import { DetailViewService } from './detail-view.service';

+import { SensorComponent } from './sensor.component';

+import { ContextService } from './context.service';

+

+@NgModule({

+  imports: [

+    MDMDetailRoutingModule,

+    MDMCoreModule,

+    // MDMFilereleaseModule

+  ],

+  declarations: [

+    MDMDetailComponent,

+    MDMDetailViewComponent,

+    MDMDescriptiveDataComponent,

+    SensorComponent,

+  ],

+  exports: [

+    MDMDetailComponent

+  ],

+  providers: [

+    DetailViewService,

+    ContextService

+  ]

+})

+export class MDMDetailModule {

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/sensor.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/details/sensor.component.html
new file mode 100644
index 0000000..6a8047b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/sensor.component.html
@@ -0,0 +1,40 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<div *ngIf="!sensors">

+  <div class="alert alert-info" style="margin: 0;">

+    <strong>{{status}}</strong>

+  </div>

+</div>

+<accordion *ngIf="sensors">

+  <accordion-group *ngFor="let node of sensors" #s>

+    <div accordion-heading class="thinheader">{{node.name}}

+      <span class="pull-right glyphicon" [ngClass]="{'glyphicon-chevron-down': s?.isOpen, 'glyphicon-chevron-right': !s?.isOpen}"></span>

+    </div>

+    <table class="table table-hover">

+      <thead>

+        <tr>

+          <th>{{LblName}}</th>

+          <th>{{LblMeasured}}</th>

+          <th>{{LblOrdered}}</th>

+        </tr>

+      </thead>

+      <tbody>

+        <tr *ngFor="let attr of node.attributes" [ngClass]="diff(attr.value[0], attr.value[1])">

+          <td>{{attr.name[0]}}</td>

+          <td>{{attr.value[0]}}</td>

+          <td>{{attr.value[1]}}</td>

+        </tr>

+      </tbody>

+    </table>

+  </accordion-group>

+</accordion>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/details/sensor.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/sensor.component.ts
new file mode 100644
index 0000000..6e25224
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/sensor.component.ts
@@ -0,0 +1,102 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import {Component, OnInit, Input, OnChanges, SimpleChange} from '@angular/core';

+import { Router, ActivatedRoute, Params } from '@angular/router';

+

+import { AccordionComponent, AccordionModule } from 'ng2-bootstrap';

+import {LocalizationService} from '../localization/localization.service';

+

+import {NodeService} from '../navigator/node.service';

+import {ContextService} from './context.service';

+import {Context, Sensor} from './context';

+import {Node} from '../navigator/node';

+import {NavigatorService} from '../navigator/navigator.service';

+

+@Component({

+  selector: 'sensors',

+  templateUrl: 'sensor.component.html',

+})

+export class SensorComponent implements OnInit {

+

+  readonly LblMeasured = 'Gemessen';

+  readonly LblName = 'Name';

+  readonly LblOrdered = 'Beauftragt';

+  readonly StatusLoading = 'Lädt..';

+  readonly StatusNoNodes = 'Keine Knoten verfügbar.';

+  readonly StatusNoDescriptiveData = 'Keine beschreibenden Daten verfügbar.';

+

+  selectedNode: Node;

+  context: String;

+

+  _diff = false;

+  contexts: Context[];

+  sensors: Sensor[];

+  errorMessage: string;

+  status: string;

+

+  uut = 'Prüfling';

+  ts = 'Testablauf';

+  te = 'Messgerät';

+  s = 'Sensoren';

+

+  constructor(private route: ActivatedRoute,

+    private localService: LocalizationService,

+              private _contextService: ContextService,

+              private navigatorService: NavigatorService) {}

+

+  ngOnInit() {

+    this.status = this.StatusLoading;

+    this.route.params

+        .subscribe(params => this.setContext(params['context'])

+    );

+

+    this.navigatorService.selectedNodeChanged

+        .subscribe(node => this.loadContext(node));

+  }

+

+  setContext(context: string) {

+    this.context = context;

+    this.loadContext(this.navigatorService.getSelectedNode());

+  }

+

+  loadContext(node: Node) {

+    if (node) {

+      this.selectedNode = node;

+      this.contexts = undefined;

+      if (node.name !== undefined && (node.type.toLowerCase() === 'measurement' || node.type.toLowerCase() === 'teststep')) {

+        this.status = this.StatusLoading;

+        this._contextService.getSensors(node).subscribe(

+            sensors => this.sensors = sensors,

+            error => this.errorMessage = <any>error);

+      } else {

+        this.status = this.StatusNoDescriptiveData;

+      }

+    } else {

+      this.status = this.StatusNoNodes;

+    }

+  }

+

+  diffToggle() {

+    this._diff = !this._diff;

+  }

+

+  diff(attr1: string, attr2: string) {

+    if (attr1 !== attr2 && this._diff) {

+      return 'danger';

+    }

+  }

+  //

+  // getTrans(type: string, attr: string) {

+  //   return this.localService.getTranslation(type, attr);

+  // }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example-routing.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example-routing.module.ts
new file mode 100644
index 0000000..0090eb7
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example-routing.module.ts
@@ -0,0 +1,29 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+import { NgModule } from '@angular/core';

+import { RouterModule, Routes } from '@angular/router';

+

+import { MDMExampleComponent } from './mdm-example.component';

+

+const moduleRoutes: Routes = [

+  { path: '', component: MDMExampleComponent }

+];

+

+@NgModule({

+  imports: [

+    RouterModule.forChild(moduleRoutes)

+  ],

+  exports: [

+    RouterModule

+  ]

+})

+export class MDMExampleRoutingModule {}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example.component.ts
new file mode 100644
index 0000000..8132789
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example.component.ts
@@ -0,0 +1,20 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+import {Component} from '@angular/core';

+

+@Component({

+  selector: 'mdm-example',

+  template: '<h1>Example Module</h1>'

+})

+export class MDMExampleComponent {

+

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example.module.ts
new file mode 100644
index 0000000..98241c3
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example.module.ts
@@ -0,0 +1,28 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+

+import { MDMCoreModule } from '../core/mdm-core.module';

+import { MDMExampleComponent } from './mdm-example.component';

+import { MDMExampleRoutingModule } from './mdm-example-routing.module';

+

+@NgModule({

+  imports: [

+    MDMCoreModule,

+    MDMExampleRoutingModule

+  ],

+  declarations: [

+    MDMExampleComponent

+  ]

+})

+export class MDMExampleModule {}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/readme.md b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/readme.md
new file mode 100644
index 0000000..bb4b896
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/readme.md
@@ -0,0 +1,27 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+To embed this example module in MDM you have to register this module in the MDMModules Module.

+

+This is done by registering a child route to **moduleRoutes** in **modules-routing.module.ts**:

+***

+ { path: 'example', loadChildren: '../example-module/mdm-example.module#MDMExampleModule'},

+***

+

+Furthermore you have to define a display name for the registered route in the array returned by **getLinks** in  **modules.component.ts**:

+***

+{ path: 'example', name: 'Example Module' }

+***

+

+For further information refer to the Angular 2 documentation for modules & router:

+* https://angular.io/docs/ts/latest/guide/ngmodule.html

+* https://angular.io/docs/ts/latest/guide/router.html

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/filerelease.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/filerelease.service.ts
new file mode 100644
index 0000000..e7541dd
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/filerelease.service.ts
@@ -0,0 +1,146 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Injectable} from '@angular/core';
+import {Http, Response, Headers, RequestOptions} from '@angular/http';
+import {Observable} from 'rxjs/Observable';
+
+import {PropertyService} from '../core/property.service';
+
+@Injectable()
+export class FilereleaseService {
+  url: string;
+  stateMap = new Array();
+  formatMap = new Array();
+  month = new Array();
+
+  constructor(private http: Http,
+              private prop: PropertyService) {
+
+    this.url = prop.getUrl('/mdm/filereleases');
+
+    this.formatMap['PAK2RAW'] = 'original Daten';
+    this.formatMap['PAK2ATFX'] = 'ATFX';
+
+    this.stateMap['RELEASE_ORDERED'] = 'beauftragt';
+    this.stateMap['RELEASE_APPROVED'] = 'genehmigt';
+    this.stateMap['RELEASE_RELEASED'] = 'freigegeben';
+    this.stateMap['RELEASE_EXPIRED'] = 'abgelaufen';
+    this.stateMap['RELEASE_PROGRESSING_ERROR'] = 'Systemfehler';
+    this.stateMap['RELEASE_PROGRESSING'] = 'In Bearbeitung';
+    this.stateMap['RELEASE_REJECTED'] = 'abgelehnt';
+
+    this.month[0] = '1';
+    this.month[1] = '2';
+    this.month[2] = '3';
+    this.month[3] = '4';
+    this.month[4] = '5';
+    this.month[5] = '6';
+    this.month[6] = '7';
+    this.month[7] = '8';
+    this.month[8] = '9';
+    this.month[9] = '10';
+    this.month[10] = '11';
+    this.month[11] = '12';
+  }
+
+  readAll() {
+    return this.read('');
+  }
+
+  readIncomming() {
+    return this.read('?direction=incomming');
+  }
+
+  readOutgoging() {
+    return this.read('?direction=outgoing');
+  }
+
+  create(release: Release) {
+    let body = JSON.stringify(release);
+    let headers = new Headers({ 'Content-Type': 'application/json' });
+    let options = new RequestOptions({ headers: headers });
+    return this.http.post(this.url, body, options)
+                    .map(this.extractData)
+                    .catch(this.handleError);
+  }
+
+  delete(release: Release) {
+    return this.http.delete(this.url + '/' + release.identifier);
+  }
+
+  approve(release: Release) {
+    release.state = 'RELEASE_APPROVED';
+    return this.update(release);
+  }
+
+  reject(release: Release) {
+    release.state = 'RELEASE_REJECTED';
+    return this.update(release);
+  }
+
+  formatDate(date) {
+    let d = new Date(date);
+    let day = d.getDate();
+    let month = this.month[d.getMonth()];
+    let year = d.getFullYear();
+    let hours = (d.getHours() < 10 ? '0' : '') + d.getHours();
+    let min = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
+    let sec = (d.getSeconds() < 10 ? '0' : '') + d.getSeconds();
+    return day + '.' + month + '.' + year + ' ' + hours + ':' + min + ':' + sec;
+  }
+
+  private read(query: string) {
+    return this.http.get(this.url + query)
+    .map(res => <Release[]> res.json().data)
+    .catch(this.handleError);
+  }
+
+  private update(release: Release) {
+    let body = JSON.stringify(release);
+    let headers = new Headers({ 'Content-Type': 'application/json' });
+    let options = new RequestOptions({ headers: headers });
+    return this.http.post(this.url + '/' + release.identifier, body, options)
+                    .map(this.extractData)
+                    .catch(this.handleError);
+  }
+
+  private handleError(error: Response) {
+    console.error(error);
+    return Observable.throw(error.json().error || 'Server error');
+  }
+
+  private extractData(res: Response) {
+    let body = res.json();
+    return body.data || { };
+  }
+}
+
+export class Release {
+  identifier: string;
+  state: string;
+  name: string;
+  sourceName: string;
+  typeName: string;
+  id: number;
+  sender: string;
+  receiver: string;
+  orderMessage: string;
+  rejectMessage: string;
+  errorMessage: string;
+  format: string;
+  fileLink: string;
+  validity: number;
+  expire: number;
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-create.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-create.component.html
new file mode 100644
index 0000000..7cd503d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-create.component.html
@@ -0,0 +1,65 @@
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
+
+<button type="button" class="btn btn-default" (click)="lgModal.show()" [disabled]="disabled" style="border-bottom-left-radius: 0; border-top-left-radius: 0;">Freigabeanfrage</button>
+
+<div bsModal #lgModal="bs-modal" class="modal fade" tabindex="-1" role="dialog">
+  <div class="modal-dialog modal-lg">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" (click)="lgModal.hide()">
+          <span aria-hidden="true">&times;</span>
+        </button>
+        <h4 class="modal-title">Freigabe</h4>
+      </div>
+      <div class="modal-body">
+        <form (ngSubmit)="createRelease()" #request="ngForm">
+          <div class="form-group">
+            <div [ngClass]="validity.valid ? 'has-success' : 'has-error'">
+              <label class="control-label col-sm-2" for="validity">Gültigkeit(Tage):</label>
+              <div class="col-sm-10">
+                <select name="validity" [(ngModel)]="release.validity" ngControl="validity" #validity="ngModel" class="form-control" required>
+                  <option *ngFor="let opt of expire" [ngValue]="opt">{{opt}}</option>
+                </select>
+              </div>
+            </div>
+
+            <div [ngClass]="format.valid ? 'has-success' : 'has-error'">
+              <label class="control-label col-sm-2" for="format">Format:</label>
+              <div class="col-sm-10">
+                <select name="format" [(ngModel)]="release.format" ngControl="format" #format="ngModel" class="form-control" required>
+                  <option *ngFor="let opt of options" [ngValue]="opt">{{getFormat(opt)}}</option>
+                </select>
+              </div>
+            </div>
+
+            <div [ngClass]="orderMessage.valid ? 'has-success' : 'has-error'">
+              <label class="control-label col-sm-2" for="orderMessage">Begründung:</label>
+              <div class="col-sm-10">
+                <textarea name="orderMessage" [(ngModel)]="release.orderMessage" ngControl="orderMessage" #orderMessage="ngModel" cols="35" rows="4" class="form-control" required></textarea>
+              </div>
+            </div>
+            <div class="row">
+              <div class="col-xs-12" style="margin-top: 15px; margin-bottom:-15px; padding-right: 30px; text-align: right;">
+                <div class="btn-group">
+                  <button type="submit" class="btn btn-default" [disabled]=!request.form.valid>Senden</button>
+                </div>
+              </div>
+            </div>
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-create.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-create.component.ts
new file mode 100644
index 0000000..46dde06
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-create.component.ts
@@ -0,0 +1,67 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Component, Input, Output, EventEmitter, ViewChild} from '@angular/core';
+import { ModalDirective } from 'ng2-bootstrap';
+import {Release, FilereleaseService} from './filerelease.service';
+import {Node} from '../navigator/node';
+
+@Component({
+  selector: 'mdm-filerelease-create',
+  templateUrl: 'mdm-filerelease-create.component.html'
+})
+export class MDMFilereleaseCreateComponent {
+
+  @Input() disabled: boolean;
+  @Input() node: Node;
+  @Output() onSubmit = new EventEmitter<boolean>();
+  release: Release = new Release;
+  errorMessage: string;
+  options = ['PAK2RAW', 'PAK2ATFX'];
+  expire = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
+  @ViewChild('lgModal')
+  lgModal: ModalDirective;
+
+  constructor(private service: FilereleaseService) {}
+
+  getFormat(key) {
+      return this.service.formatMap[key];
+  }
+
+  createRelease() {
+    this.release.identifier = '';
+    this.release.state = '';
+    this.release.name = this.node.name;
+    this.release.sourceName = this.node.sourceName;
+    this.release.typeName = this.node.type;
+    this.release.id = this.node.id;
+    this.release.sender = '';
+    this.release.receiver = '';
+    this.release.rejectMessage = '';
+    this.release.errorMessage = '';
+    this.release.fileLink = '';
+    this.release.expire = 0;
+    this.service.create(this.release).subscribe(
+      release => this.release = release,
+      error => this.errorMessage = <any>error);
+    this.clear();
+    this.onSubmit.emit(true);
+    this.lgModal.hide();
+  }
+
+  clear() {
+    this.release = new Release();
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-display.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-display.component.html
new file mode 100644
index 0000000..29ac019
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-display.component.html
@@ -0,0 +1,48 @@
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
+
+<table class="table table-bordered">
+  <tbody>
+    <tr>
+      <td>Versuchsname:</td><td>{{release.name}}</td>
+    </tr>
+    <tr>
+      <td>Fehlerbericht:</td><td>{{release.errorMessage}}</td>
+    </tr>
+    <tr>
+      <td>freigegeben bis:</td><td>{{getDate(release.expire)}}</td>
+    </tr>
+    <tr>
+      <td>Format:</td><td>{{getFormat(release.format)}}</td>
+    </tr>
+    <tr>
+      <td>Begründung für Freigabe:</td><td>{{release.orderMessage}}</td>
+    </tr>
+    <tr>
+      <td>Begründung für Ablehnung:</td><td>{{release.rejectMessage}}</td>
+    </tr>
+    <tr>
+      <td>Antragsteller:</td><td>{{release.sender}}</td>
+    </tr>
+    <tr>
+      <td>Messungsverantwortlicher:</td><td>{{release.receiver}}</td>
+    </tr>
+    <tr>
+      <td>Status:</td><td>{{getState(release.state)}}</td>
+    </tr>
+    <tr>
+      <td>Freigabedauer:</td><td>{{release.validity}} Tage</td>
+    </tr>
+  </tbody>
+</table>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-display.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-display.component.ts
new file mode 100644
index 0000000..599d028
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-display.component.ts
@@ -0,0 +1,41 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Component, Input} from '@angular/core';
+import {Release, FilereleaseService} from './filerelease.service';
+
+@Component({
+  selector: 'mdm-filerelease-display',
+  templateUrl: 'mdm-filerelease-display.component.html',
+  styles: [],
+  providers: []
+})
+export class MDMFilereleaseDisplayComponent {
+  @Input() release: Release;
+
+  constructor(private service: FilereleaseService) {}
+
+  getFormat(format) {
+    return this.service.formatMap[format];
+  }
+
+  getState(state) {
+    return this.service.stateMap[state];
+  }
+
+  getDate(date) {
+    if (date === 0) { return; }
+    return this.service.formatDate(date);
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/filerelease/mdm-filerelease.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.component.html
similarity index 78%
rename from org.eclipse.mdm.application/src/main/webapp/templates/filerelease/mdm-filerelease.component.html
rename to org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.component.html
index 1997876..accbdd9 100644
--- a/org.eclipse.mdm.application/src/main/webapp/templates/filerelease/mdm-filerelease.component.html
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.component.html
@@ -1,16 +1,24 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
 
+<style>
+  .container-fluid {
+    padding: 0;
+  }
+</style>
 <div class="container-fluid">
-  <div class="box1">
+  <div class="box" style="margin-bottom: 15px;">
     <div class="well">
       <h2>Freigabeantragseingang</h2>
     </div>
@@ -41,7 +49,7 @@
   </div>
 </div>
 <div class="container-fluid">
-  <div class="box2">
+  <div class="box">
     <div class="well">
       <h2>Freigabeantragsausgang</h2>
     </div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.component.ts
new file mode 100644
index 0000000..7df0f68
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.component.ts
@@ -0,0 +1,114 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Component, OnInit, ViewChild} from '@angular/core';
+import {FilereleaseService, Release} from './filerelease.service';
+import {MDMFilereleaseDisplayComponent} from './mdm-filerelease-display.component';
+import {PropertyService} from '../core/property.service';
+import { ModalDirective } from 'ng2-bootstrap';
+
+@Component({
+  selector: 'mdm-filerelease',
+  templateUrl: 'mdm-filerelease.component.html',
+  styles: ['.box {border: 1px solid #ddd; border-radius: 4px;}']
+})
+export class MDMFilereleaseComponent implements OnInit {
+
+  incoming: Release[] = [];
+  outgoing: Release[] = [];
+  errorMessage: string;
+  release: Release = new Release;
+  event = 'display';
+  dataHost: string;
+
+  @ViewChild('lgModal')
+  lgModal: ModalDirective;
+
+  @ViewChild('smModal')
+  smModal: ModalDirective;
+
+  constructor(private service: FilereleaseService,
+              private prop: PropertyService) {
+    this.dataHost = prop.getDataHost();
+  }
+
+  ngOnInit() {
+    this.getReleases();
+  }
+
+  getReleases() {
+    this.service.readOutgoging().subscribe(
+      releases => this.outgoing = releases,
+      error => this.errorMessage = <any>error);
+    this.service.readIncomming().subscribe(
+      releases => this.incoming = releases,
+      error => this.errorMessage = <any>error);
+  }
+  setData(release) {
+    this.release = release;
+  }
+  getState(release: Release) {
+    if (release.state === 'RELEASE_ORDERED') { return 'info'; }
+    if (release.state === 'RELEASE_APPROVED') { return 'warning'; }
+    if (release.state === 'RELEASE_RELEASED') { return 'success'; }
+    if (release.state === 'RELEASE_EXPIRED') { return 'danger'; }
+    if (release.state === 'RELEASE_REJECTED') { return 'danger'; }
+    if (release.state === 'RELEASE_PROGRESSING_ERROR') { return 'danger'; }
+    if (release.state === 'RELEASE_PROGRESSING') { return 'warning'; }
+    return 'info';
+  }
+  rejectRelease(reason: string) {
+    this.release.rejectMessage = reason;
+    this.service.reject(this.release).subscribe(
+      release => this.updateList(release),
+      error => this.errorMessage = <any>error);
+  }
+  approveRelease() {
+    this.service.approve(this.release).subscribe(
+      release => this.updateList(release),
+      error => this.errorMessage = <any>error);
+    this.release.state = 'RELEASE_PROGRESSING';
+  }
+  updateList(id) {
+    let pos = this.outgoing.map(function(e) { return e.identifier; }).indexOf(id);
+    if (pos !== -1) { this.outgoing.splice(pos, 1); }
+    pos = this.incoming.map(function(e) { return e.identifier; }).indexOf(id);
+    if (pos !== -1) { this.incoming.splice(pos, 1); }
+  }
+  isDeletable() {
+    if (this.release.state !== 'RELEASE_PROGRESSING') { return false; }
+    return true;
+  }
+  deleteRelease() {
+    let id = this.release.identifier;
+    this.service.delete(this.release).subscribe(data => this.updateList(id), err => console.log(err));
+  }
+  setEvent(event) {
+    this.event = event;
+  }
+  isReleaseable() {
+    if (this.release.state !== 'RELEASE_ORDERED') { return true; }
+    return false;
+  }
+  getFormat(format) {
+    return this.service.formatMap[format];
+  }
+  getTransState(state) {
+    return this.service.stateMap[state];
+  }
+  getDate(date) {
+    if (date === 0) { return; }
+    return this.service.formatDate(date);
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.module.ts
new file mode 100644
index 0000000..937c0bf
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.module.ts
@@ -0,0 +1,37 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+

+import { MDMCoreModule } from '../core/mdm-core.module';

+

+import { MDMFilereleaseComponent } from './mdm-filerelease.component';

+import { MDMFilereleaseCreateComponent } from './mdm-filerelease-create.component';

+import { MDMFilereleaseDisplayComponent } from './mdm-filerelease-display.component';

+

+@NgModule({

+  imports: [

+    MDMCoreModule

+  ],

+  declarations: [

+    MDMFilereleaseComponent,

+    MDMFilereleaseCreateComponent,

+    MDMFilereleaseDisplayComponent,

+  ],

+  exports: [

+    MDMFilereleaseComponent,

+    MDMFilereleaseCreateComponent,

+    MDMFilereleaseDisplayComponent,

+  ]

+})

+export class MDMFilereleaseModule {

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/localization/localization.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/localization/localization.service.ts
new file mode 100644
index 0000000..eab4dbe
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/localization/localization.service.ts
@@ -0,0 +1,98 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Injectable, OnInit} from '@angular/core';
+import {Http, Response} from '@angular/http';
+import {Observable} from 'rxjs/Observable';
+import {Localization} from './localization';
+import {Node} from '../navigator/node';
+import {NodeService} from '../navigator/node.service';
+import {PropertyService} from '../core/property.service';
+
+@Injectable()
+export class LocalizationService implements OnInit {
+
+  private _nodeUrl: string;
+
+  private _cache: Localization[] = [];
+  private errorMessage: string;
+
+  constructor(private http: Http,
+              private _prop: PropertyService,
+              private _node: NodeService) {
+
+    this._nodeUrl = _prop.getUrl('/mdm/environments');
+  }
+
+  ngOnInit() {
+    let node: Node;
+    this._node.getNodes(node).subscribe(
+      envs => this.initLocalization(envs),
+      error => this.errorMessage = <any>error);
+  }
+
+  getTranslation(type: string, comp: string) {
+    let trans: string;
+    if (comp) {
+      trans = type + '.' + comp;
+    } else {
+      trans = type;
+    }
+    let pos = this._cache.map(function(e) { return e.name; }).indexOf(trans);
+    if (pos !== -1) {
+      return this._cache[pos].localizedName;
+    }
+    return trans;
+  }
+
+  private initLocalization(envs: Node[]) {
+    envs.forEach((env) => {
+      this.getLocalization(env).subscribe(
+        locals => this.mergeLocalizations(locals),
+        error => this.errorMessage = <any>error);
+    });
+  }
+
+  private mergeLocalizations(locals: Localization[]) {
+    let t_local = this._cache;
+    locals.forEach(function(local) {
+      let pos = t_local.map(function(e) { return e.name; }).indexOf(local.name);
+      if (pos === -1) {
+        t_local.push(local);
+      }
+    });
+    this._cache = t_local;
+  }
+
+  private getLocalization(node: Node) {
+    let url = this._nodeUrl + '/' + node.sourceName;
+    if (node.sourceType === 'Environment') {
+      url = url + '/localizations?all=true';
+    } else {
+      url = url + '/' + node.type.toLowerCase() + 's/localizations';
+    }
+    return this.get(url);
+  }
+
+  private get(url: string) {
+    return this.http.get(url)
+    .map(res => <Localization[]> res.json().data)
+    .catch(this.handleError);
+  }
+
+  private handleError(error: Response) {
+    console.error(error);
+    return Observable.throw(error.json().error || 'Server error');
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/localization/localization.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/localization/localization.ts
new file mode 100644
index 0000000..aaa4c9d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/localization/localization.ts
@@ -0,0 +1,18 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+export class Localization {
+  name: string;
+  localizedName: string;
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules-routing.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules-routing.module.ts
new file mode 100644
index 0000000..9ef1545
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules-routing.module.ts
@@ -0,0 +1,35 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+import { RouterModule, Routes } from '@angular/router';

+

+import { MDMModulesComponent } from '../modules/mdm-modules.component';

+import { MDMSearchComponent } from '../search/mdm-search.component';

+

+const moduleRoutes: Routes = [

+  { path: '', component: MDMModulesComponent, children: [

+    { path: '', redirectTo: 'search', pathMatch: 'full' },

+    { path: 'details', loadChildren: '../details/mdm-detail.module#MDMDetailModule'},

+    { path: 'search', component: MDMSearchComponent },

+  ]}

+];

+

+@NgModule({

+  imports: [

+    RouterModule.forChild(moduleRoutes)

+  ],

+  exports: [

+    RouterModule

+  ]

+})

+export class MDMModulesRoutingModule {}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.component.html
new file mode 100644
index 0000000..202cac7
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.component.html
@@ -0,0 +1,21 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<ul class="nav nav-tabs">

+  <li *ngFor="let m of links" [routerLinkActive]="['active']" role="presentation"><a routerLink="{{m.path}}" style="cursor:pointer;"> {{m.name}}</a></li>

+</ul>

+

+<div class="tab-content" style="border: 1px solid #ddd; border-top: 0; padding: 1em;">
+  <div class="tab-pane active">

+    <router-outlet></router-outlet>

+  <div>

+</div>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.component.ts
new file mode 100644
index 0000000..692052c
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.component.ts
@@ -0,0 +1,28 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import {Component} from '@angular/core';

+import {Router} from '@angular/router';

+

+@Component({

+  selector: 'modules',

+  templateUrl: 'mdm-modules.component.html',

+  providers: []

+})

+export class MDMModulesComponent {

+

+  links = [

+    { name: 'Details', path: 'details'},

+    { name: 'MDM Suche', path: 'search'}

+  ];

+  constructor(private router: Router) {}

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.module.ts
new file mode 100644
index 0000000..3acb147
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.module.ts
@@ -0,0 +1,36 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+

+import { MDMModulesComponent } from './mdm-modules.component';

+import { MDMModulesRoutingModule } from './mdm-modules-routing.module';

+

+import { MDMCoreModule } from '../core/mdm-core.module';

+import { MDMDetailModule } from '../details/mdm-detail.module';

+import { MDMSearchModule } from '../search/mdm-search.module';

+

+@NgModule({

+  imports: [

+    MDMCoreModule,

+    MDMModulesRoutingModule,

+    MDMDetailModule,

+    MDMSearchModule

+  ],

+  declarations: [

+    MDMModulesComponent

+  ],

+  exports: [

+    MDMModulesComponent,

+  ]

+})

+export class MDMModulesModule {}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view-routing.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view-routing.module.ts
new file mode 100644
index 0000000..f49e2e6
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view-routing.module.ts
@@ -0,0 +1,42 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+import { RouterModule, Routes } from '@angular/router';

+

+import { MDMNavigatorViewComponent } from './mdm-navigator-view.component';

+import { MDMDetailComponent } from '../details/mdm-detail.component';

+import { MDMSearchComponent } from '../search/mdm-search.component';

+import { MDMModulesComponent } from '../modules/mdm-modules.component';

+

+const navigatorViewRoutes: Routes = [

+  { path: 'navigator', component: MDMNavigatorViewComponent,

+    children: [

+      { path: '**', component: MDMModulesComponent },

+      { path: 'details', component: MDMDetailComponent},

+      { path: 'modules', component: MDMModulesComponent, children: [

+        { path: 'details', component: MDMDetailComponent},

+        { path: 'filerelease', component: MDMDetailComponent},

+        { path: 'search',  component: MDMSearchComponent }

+      ]}

+    ]}

+];

+

+@NgModule({

+  imports: [

+    RouterModule.forChild(navigatorViewRoutes)

+  ],

+  exports: [

+    RouterModule

+  ]

+})

+export class MDMNavigatorViewRoutingModule {}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.css b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.css
new file mode 100644
index 0000000..69a2dc6
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.css
@@ -0,0 +1,60 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+.panel-body {

+  padding: 0;

+}

+.list-group {

+  margin-bottom: 0;

+}

+.list-group-item {

+  white-space: nowrap;

+}

+.list-group-item:first-child {

+  border-top-left-radius: 0;

+  border-top-right-radius: 0;

+}

+.list-group-item:last-child {

+  border-bottom-right-radius: 0;

+  border-bottom-left-radius: 0;

+  border-bottom-style: none;

+}

+

+

+.navigator {

+  position: relative;

+  margin: 0;

+}

+

+.navigator-content {

+  margin-left: 8px;

+  min-height: calc(100vh - 53px);
+  max-height: calc(100vh - 53px);

+  overflow: auto;

+}

+

+.navigator .navbar-right {

+  position: absolute;

+  right: 15px;

+  z-index: 1;

+}

+

+.navigator .navbar-header {

+  position: relative;

+  background-color: #f8f8f8;

+  z-index: 2;

+}

+

+vertical-split-separator {

+  margin: 0;

+  border: none !important;

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.html
new file mode 100644
index 0000000..b3e6dc3
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.html
@@ -0,0 +1,52 @@
+<!--****************************************************************************

+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *

+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Dennis Schroeder - initial implementation                                   *

+*  Matthias Koller, Johannes Stamm - additional client functionality           *

+*****************************************************************************-->

+

+<vertical-split-pane primary-component-minsize="{{minWidthLeft()}}" secondary-component-minsize="{{minWidthRight()}}" primary-component-initialratio="{{initRatio()}}">

+  <div class="split-pane-content-primary">

+    <nav class="navigator">

+      <div class="navbar navbar-default container-fluid">

+        <div class="navbar-header">

+          <a class="navbar-brand" (click)="activate('Navigation')" style="cursor:pointer;">{{LblNavigator}}</a>

+        </div>

+        <div>

+          <ul class="nav navbar-nav navbar-right">

+            <li [ngClass]="isDropActive('Dropdown')" title="{{TtlSelectNodeprovider}}" dropdown>

+              <a (click)="activate('Dropdown')" class="dropdown-toggle" dropdownToggle aria-haspopup="true" aria-expanded="false" style="cursor:pointer;">

+                  {{activeNodeprovider.name}}

+                <em class="caret" ></em>

+              </a>

+              <ul class="dropdown-menu" dropdownMenu>

+                <li *ngFor="let np of getNodeproviders()">

+                  <a class="dropdown-item" (click)="activateNodeProvider(np)" style="cursor:pointer;">

+                      {{np.name}}

+                  </a>

+                </li>

+              </ul>

+            </li>

+          </ul>

+        </div>

+      </div>

+      <mdm-navigator></mdm-navigator>

+    </nav>

+  </div>

+  <div class="split-pane-content-secondary">

+    <div class="navigator-content" (scroll)=onScroll($event)>

+      <router-outlet></router-outlet>

+      <mdm-basket (onSelect)="updateSelectedNode($event)" [activeNode]=activeNode (onActive)="updateActiveNode($event)"></mdm-basket>

+      <div *ngIf="scrollBtnVisible" style="position: fixed; bottom: 30px; right: 35px;">

+        <button class="btn btn-default" (click)="onScrollTop()" style="z-index: 10000;"><span class="glyphicon glyphicon-arrow-up" style="z-index: 10000;" title="{{TtlScrollUp}}"></span></button>

+      </div>

+    </div>

+  </div>

+</vertical-split-pane>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.ts
new file mode 100644
index 0000000..1d3e4d4
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.ts
@@ -0,0 +1,114 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Component, ViewEncapsulation, OnInit, OnDestroy} from '@angular/core';
+
+import { DropdownModule, AccordionConfig, DropdownConfig } from 'ng2-bootstrap';
+
+import {NodeService} from '../navigator/node.service';
+import {Node} from '../navigator/node';
+import {NodeproviderService} from '../navigator/nodeprovider.service';
+import { SplitPaneModule } from 'ng2-split-pane/lib/ng2-split-pane';
+
+@Component({
+  selector: 'mdm-navigator-view',
+  templateUrl: 'mdm-navigator-view.component.html',
+  styleUrls: [ './mdm-navigator-view.component.css' ],
+  providers: [DropdownConfig, AccordionConfig],
+  encapsulation: ViewEncapsulation.None
+})
+export class MDMNavigatorViewComponent implements OnInit, OnDestroy {
+
+  readonly LblNavigator = 'Navigator';
+  readonly TtlScrollUp = 'Zum Seitenanfang';
+  readonly TtlSelectNodeprovider = 'Baumdarstellung auswählen';
+
+  selectedNode = new Node;
+  activeNode: Node;
+  closeOther = false;
+
+  activeNodeprovider: any;
+  _comp = 'Navigation';
+  subscription: any;
+
+  div: any;
+  scrollBtnVisible = false;
+
+  constructor(private nodeProviderService: NodeproviderService) {}
+
+  onScrollTop() {
+    this.div.scrollTop = 0;
+  }
+
+  onScroll(event: any) {
+    if (event.target.scrollTop > 0) {
+      this.scrollBtnVisible = true;
+    } else {
+      this.scrollBtnVisible = false;
+    }
+    this.div = event.target;
+  }
+
+  updateSelectedNode(node: Node) {
+    this.selectedNode = node;
+  }
+  updateActiveNode(node: Node) {
+    this.activeNode = node;
+  }
+  activateNodeProvider(nodeprovider: any) {
+    this.nodeProviderService.setActiveNodeprovider(nodeprovider);
+  }
+
+  getNodeproviders() {
+    return this.nodeProviderService.getNodeproviders();
+  }
+
+  ngOnInit() {
+    this.activeNodeprovider = this.nodeProviderService.getActiveNodeprovider();
+    this.subscription = this.nodeProviderService.nodeProviderChanged
+        .subscribe(np => this.activeNodeprovider = np);
+  }
+
+  ngOnDestroy() {
+    this.subscription.unsubscribe();
+  }
+
+  activate(comp: string) {
+    this._comp = comp;
+  }
+
+  isActive(comp: string) {
+    if (comp === this._comp) {
+      return 'active';
+    }
+  }
+
+  isDropActive(comp: string) {
+    if (comp === this._comp) {
+      return 'open ';
+    }
+  }
+
+   minWidthLeft() {
+     return 180;
+   }
+
+   minWidthRight() {
+     return 0.20 * window.innerWidth;
+   }
+
+   initRatio() {
+     return Math.max(250 / window.innerWidth, 0.20);
+   }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.module.ts
new file mode 100644
index 0000000..2d4f2b1
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.module.ts
@@ -0,0 +1,43 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+

+import { MDMCoreModule } from '../core/mdm-core.module';

+

+import { MDMNavigatorViewRoutingModule } from './mdm-navigator-view-routing.module';

+

+import { MDMNavigatorViewComponent } from './mdm-navigator-view.component';

+

+import { MDMNavigatorModule } from '../navigator/mdm-navigator.module';

+import { MDMModulesModule } from '../modules/mdm-modules.module';

+import { MDMBasketModule } from '../basket/mdm-basket.module';

+import { SplitPaneModule } from 'ng2-split-pane/lib/ng2-split-pane';

+

+@NgModule({

+  imports: [

+    MDMCoreModule,

+    MDMNavigatorViewRoutingModule,

+    MDMNavigatorModule,

+    MDMModulesModule,

+    MDMBasketModule,

+    SplitPaneModule

+  ],

+  declarations: [

+    MDMNavigatorViewComponent

+  ],

+  exports: [

+    MDMNavigatorViewRoutingModule

+  ]

+})

+export class MDMNavigatorViewModule {

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/defaultnodeprovider.json b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/defaultnodeprovider.json
new file mode 100644
index 0000000..fc6d39c
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/defaultnodeprovider.json
@@ -0,0 +1,40 @@
+{

+  "name" : "Default",

+  "type" : "Environment",

+  "children" : {

+	"type" : "Project",

+	"attribute" : "Name",

+	"query" : "/projects",

+	"children" : {

+	  "type" : "Pool",

+	  "attribute" : "Name",

+	  "query" : "/pools?filter=Project.Id eq {Project.Id}",

+	  "children" : {

+		"type" : "Test",

+		"attribute" : "Name",

+		"query" : "/tests?filter=Pool.Id eq {Pool.Id}",

+		"children" : {

+		  "type" : "TestStep",

+		  "attribute" : "Name",

+		  "query" : "/teststeps?filter=Test.Id eq {Test.Id}",

+		  "children" : {

+			"type" : "Measurement",

+			"attribute" : "Name",

+			"query" : "/measurements?filter=TestStep.Id eq {TestStep.Id}",

+			"children" : {

+			  "type" : "ChannelGroup",

+			  "attribute" : "Name",

+			  "query" : "/channelgroups?filter=Measurement.Id eq {Measurement.Id}",

+			  "caption" : "ChannelGroup.Name",

+			  "children" : {

+				  "type" : "Channel",

+				  "attribute" : "Name",

+				  "query" : "/channels?filter=ChannelGroup.Id eq {ChannelGroup.Id}"

+			  }

+			}

+		  }

+		}

+	  }

+	}

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.MockNodes.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.MockNodes.ts
new file mode 100644
index 0000000..d8ee3b9
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.MockNodes.ts
@@ -0,0 +1,98 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+export const MockEnvNodes = {
+  'type': 'Environment',
+  'data': [
+    {
+      'name': 'Test Environment',
+      'id': 1,
+      'type': 'Environment',
+      'sourceType': 'Environment',
+      'sourceName': 'Test Environment',
+      'attributes': [
+        {
+          'name': 'Timezone',
+          'value': 'GMT',
+          'unit': '',
+          'dataType': 'STRING'
+        }
+      ]
+    }
+  ]
+};
+
+export const MockTestNodes = {
+    'type': 'Test',
+    'data': [
+      {
+        'name': 'Test 1',
+        'id': 10,
+        'type': 'Test',
+        'sourceType': 'Test',
+        'sourceName': 'Test Environment',
+        'attributes': [
+          {
+            'name': 'DateClosed',
+            'value': '',
+            'unit': '',
+            'dataType': 'DATE'
+          }
+        ]
+      },
+      {
+        'name': 'Test 2',
+        'id': 20,
+        'type': 'Test',
+        'sourceType': 'Test',
+        'sourceName': 'Test Environment',
+        'attributes': [
+          {
+            'name': 'DateClosed',
+            'value': '',
+            'unit': '',
+            'dataType': 'DATE'
+          }
+        ]
+      },
+      {
+        'name': 'Test 3',
+        'id': 30,
+        'type': 'Test',
+        'sourceType': 'Test',
+        'sourceName': 'Test Environment',
+        'attributes': [
+          {
+            'name': 'DateClosed',
+            'value': '',
+            'unit': '',
+            'dataType': 'DATE'
+          }
+        ]
+      }
+    ]
+};
+
+export const MockNodeProvider = {
+  'name' : 'Channels',
+  'type' : 'Environment',
+  'children' : {
+    'type' : 'Test',
+    'attribute' : 'Name',
+    'query' : '/tests',
+    'children' : {
+      'type' : 'Channel',
+      'attribute' : 'Name',
+      'query' : '/channels?filter=Test.Id eq {Test.Id}'
+    }
+  }
+};
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.html
new file mode 100644
index 0000000..ead46b9
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.html
@@ -0,0 +1,28 @@
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
+
+<p-contextMenu #cm [model]="contextMenuItems" appendTo="body"></p-contextMenu>
+
+<p-tree class="mdmtree"
+    [value]="nodes"
+    selectionMode="multiple"
+    [(selection)]="selectedNodes"
+    (onNodeExpand)="loadNode($event)"
+    (onNodeSelect)="nodeSelect($event)"
+    [contextMenu]="cm"
+    >
+  <template let-node pTemplate="default">
+        <span [title]="node.label" [id]="getId(node)">{{ node.label }}</span>
+  </template>
+</p-tree>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.spec.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.spec.ts
new file mode 100644
index 0000000..0e98bbb
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.spec.ts
@@ -0,0 +1,76 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+import { DebugElement } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import {Observable} from 'rxjs/Observable';
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { Node } from './node';
+import { MockEnvNodes, MockNodeProvider } from './mdm-navigator.MockNodes';
+import { NodeService } from './node.service';
+import { NavigatorService } from './navigator.service';
+import { MDMNavigatorComponent } from './mdm-navigator.component';
+/*
+describe ( 'Navigator Tree, navigator component', () => {
+
+    class NodeServiceMock {
+        nodeProviderChanged: Observable<any> = Observable.of(MockNodeProvider);
+
+        getNodes(): Observable<Node[]> { return Observable.of(MockEnvNodes.data); };
+
+        compareNode() { return true; };
+    }
+
+    let fixture: ComponentFixture<MDMNavigatorComponent>;
+    let comp: MDMNavigatorComponent;
+    let de, listElement_span, listElement_a: DebugElement;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [MDMNavigatorComponent,
+                           MDMNodeProviderComponent
+                           ],
+            providers: [NavigatorService],
+            imports: []
+        });
+        TestBed.overrideComponent(MDMNavigatorComponent, {
+            set: {
+                providers: [{provide: NodeService, useClass: NodeServiceMock}]
+            }
+        });
+
+        fixture = TestBed.createComponent(MDMNavigatorComponent);
+        comp = fixture.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should show environment Node', () => {
+       expect(comp.nodes).toEqual(MockEnvNodes.data);
+       de = fixture.debugElement.query(By.css('li'));
+       expect(de.nativeElement.textContent).toContain(comp.nodes[0].name);
+    });
+
+    it('should open Node after click and emit active node', () => {
+        listElement_span = fixture.debugElement.query(By.css('span'));
+        listElement_span.nativeElement.click();
+        expect(comp.nodes[0].active).toBeTruthy();
+
+        listElement_a = fixture.debugElement.query(By.css('a'));
+        listElement_a.nativeElement.click();
+        comp.onActive.subscribe(node => {
+            expect(node).toEqual(comp.nodes);
+        });
+     });
+});
+*/
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.ts
new file mode 100644
index 0000000..b8d991d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.ts
@@ -0,0 +1,222 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Component, OnInit} from '@angular/core';
+import {TreeModule, TreeNode, ContextMenuModule, MenuItem} from 'primeng/primeng';
+
+import {MDMItem} from '../core/mdm-item';
+import {Node} from '../navigator/node';
+import {NodeService} from '../navigator/node.service';
+import {QueryService} from '../tableview/query.service';
+import {NodeproviderService} from './nodeprovider.service';
+import {NavigatorService} from './navigator.service';
+import {BasketService} from '../basket/basket.service';
+
+import {Observable} from 'rxjs/Observable';
+
+@Component({
+  selector: 'mdm-navigator',
+  templateUrl: './mdm-navigator.component.html',
+  styles: [
+    '>>>.ui-tree { overflow: auto; max-height: calc(100vh - 7em); min-height: calc(100vh - 7em); '
+    + 'padding: .25em .25em .5em .25em !important; }',
+    '>>>.ui-tree .ui-tree-container { overflow: visible; }'
+  ]
+})
+export class MDMNavigatorComponent implements OnInit {
+
+  readonly AlrtItemTypeNotSupported = 'Dieser Knotentyp wird von der aktuellen Ansicht nicht angezeigt!';
+
+  selectedNodes: TreeNode[] = [];
+  nodes: TreeNode[] = [];
+  lastClickTime = 0;
+
+  loadingNode = <TreeNode>{
+    label: 'Unterpunkte werden geladen.',
+    leaf: true,
+    icon: 'fa fa-spinner fa-pulse fa-3x fa-fw'
+  };
+
+  contextMenuItems: MenuItem[] = [
+    { label: 'In Warenkorb legen', icon: 'glyphicon glyphicon-shopping-cart', command: (event) => this.addSelectionToBasket() }
+  ];
+
+  constructor(private nodeService: NodeService,
+    private queryService: QueryService,
+    private basketService: BasketService,
+    private nodeproviderService: NodeproviderService,
+    private navigatorService: NavigatorService) {
+  }
+
+  ngOnInit() {
+    this.reloadTree();
+    this.nodeproviderService.nodeProviderChanged
+      .subscribe(np => this.reloadTree());
+
+    this.navigatorService.selectedNodeChanged
+      .subscribe(node => this.selectNode(node));
+
+    this.navigatorService.onOpenInTree
+      .subscribe(items => this.openInTree(items));
+  }
+
+  nodeSelect(event) {
+    if (event.node.data) {
+      this.navigatorService.setSelectedItem(event.node.data);
+    }
+    if (event.originalEvent.timeStamp - this.lastClickTime < 300) {
+      if (!event.node.expanded && !event.node.children) {
+        this.loadNode(event);
+      }
+      event.node.expanded = !event.node.expanded;
+    }
+    this.lastClickTime = event.originalEvent.timeStamp;
+  }
+
+  loadNode(event) {
+    if (event.node && event.node.children === undefined && !event.node.leaf) {
+      return this.getChildren(event.node)
+        .startWith([this.loadingNode])
+        .do(nodes => (nodes && nodes.length === 0) ? event.node.leaf = true : event.node.leaf = false)
+        .subscribe(nodes => event.node.children = nodes);
+    }
+  }
+
+  reloadTree() {
+    this.nodeService.getRootNodes().subscribe(n => {
+      this.nodes = n.map(node => this.mapNode(node));
+    });
+  }
+
+  onFocus(event: { eventName: string, node: TreeNode }) {
+    this.navigatorService.setSelectedItem(event.node.data.item);
+  }
+
+  getNodeClass(item: MDMItem) {
+    return 'icon ' + item.type.toLowerCase();
+  }
+
+  mapNode(node: Node) {
+    let item = new MDMItem(node.sourceName, node.type, +node.id);
+    return <TreeNode>{
+      label: node.name,
+      leaf: this.nodeproviderService.getSubNodeprovider(item) === undefined,
+      data: item,
+      icon: this.getNodeClass(item)
+    };
+  }
+
+  getChildren(node: TreeNode) {
+    return this.nodeService.getNodesByUrl(this.nodeproviderService.getQueryForChildren(node.data))
+      .map(nodes => nodes.map(n => this.mapNode(n)))
+      .map(treenodes => treenodes.sort((n1, n2) => n1.label.localeCompare(n2.label)));
+  }
+
+  selectNode(event) {
+  }
+
+  addSelectionToBasket() {
+    this.basketService.addAll(this.selectedNodes.map(node => <MDMItem>node.data));
+  }
+
+  typeToUrl(type: string) {
+    switch (type) {
+      case 'StructureLevel':
+        return 'pools';
+      case 'MeaResult':
+        return 'measurements';
+      case 'SubMatrix':
+        return 'channelgroups';
+      case 'MeaQuantity':
+        return 'channels';
+      default:
+        return type.toLowerCase() + 's';
+    }
+  }
+
+  openInTree(items: MDMItem[]) {
+    this.selectedNodes = [];
+    items.forEach(item => {
+      let pathTypes = this.nodeproviderService.getPathTypes(item.type);
+      if (pathTypes.length === 0) {
+        alert(this.AlrtItemTypeNotSupported);
+      } else {
+        let env = this.nodes.find(e => item.source === e.data.source);
+        env.expanded = true;
+        this.openChildrenRecursive(item, env, pathTypes, 1);
+      }
+    });
+  }
+
+  openChildrenRecursive(item: MDMItem, current: TreeNode, pathTypes: string[], iii: number) {
+    if (current.children) {
+      this.expander(item, current, pathTypes, iii);
+    } else {
+      this.getChildren(current)
+        .startWith([this.loadingNode])
+        .do(n => current.children = n)
+        .subscribe(() => {
+          this.expander(item, current, pathTypes, iii);
+        });
+    }
+  }
+
+  expander(item: MDMItem, current: TreeNode, pathTypes: string[], iii: number) {
+    let expandList: number[] = [];
+    this.nodeService.searchNodes('filter=' + item.type + '.Id eq ' + item.id,
+      item.source, this.typeToUrl(pathTypes[iii]))
+      .subscribe(nodes => {
+        expandList = nodes.map(n => n.id);
+        current.children.filter(node => expandList.findIndex(
+          i => node.data ? i === node.data.id : false) > -1
+        )
+        .forEach(node => {
+          if (++iii < pathTypes.length) {
+            node.expanded = true;
+            this.openChildrenRecursive(item, node, pathTypes, iii);
+            this.scrollToSelectionPrimeNgDataTable(node);
+          } else {
+            this.selectedNodes.push(node);
+            let length = this.selectedNodes.length;
+            if (length === 1) {
+              this.nodeService.getNodeFromItem(this.selectedNodes[length - 1].data)
+                  .subscribe( n => this.navigatorService.setSelectedNode(n));
+            };
+            this.scrollToSelectionPrimeNgDataTable(node);
+          }
+        });
+      });
+  }
+
+  /**
+   * PrimeNG does not support scroll to view. This methods implements a
+   * workaround by using HTML element IDs.
+   * @see https://github.com/primefaces/primeng/issues/1278
+   */
+  scrollToSelectionPrimeNgDataTable(node: TreeNode) {
+    let list = document.querySelectorAll('p-tree span#' + this.getId(node));
+
+    if (list && list.length > 0) {
+        list.item(0).scrollIntoView();
+    }
+  }
+
+  getId(node: TreeNode) {
+    if (node && node.data) {
+      return 'node_' + node.data.source + '_' + node.data.type + '_' + node.data.id;
+    } else {
+      return '';
+    }
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.module.ts
new file mode 100644
index 0000000..5caa4c3
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.module.ts
@@ -0,0 +1,31 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+

+import { MDMCoreModule } from '../core/mdm-core.module';

+

+import { MDMNavigatorComponent } from './mdm-navigator.component';

+

+@NgModule({

+  imports: [

+    MDMCoreModule

+  ],

+  declarations: [

+    MDMNavigatorComponent

+  ],

+  exports: [

+    MDMNavigatorComponent

+  ]

+})

+export class MDMNavigatorModule {

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/navigator.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/navigator.service.ts
new file mode 100644
index 0000000..838086b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/navigator.service.ts
@@ -0,0 +1,56 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import {Injectable, Output, EventEmitter} from '@angular/core';

+import {Http, Response, Headers, RequestOptions} from '@angular/http';

+import {Observable} from 'rxjs/Observable';

+

+import {MDMItem} from '../core/mdm-item';

+import {Node} from './node';

+import {PropertyService} from '../core/property.service';

+import {NodeService} from './node.service';

+

+

+@Injectable()

+export class NavigatorService {

+

+  @Output() onOpenInTree = new EventEmitter<MDMItem[]>();

+

+  public selectedNodeChanged: EventEmitter<Node> = new EventEmitter<Node>();

+  private selectedNode: Node;

+

+  constructor(private nodeService: NodeService) {

+

+  }

+

+  setSelectedNode(node: Node) {

+    this.selectedNode = node;

+    this.fireSelectedNodeChanged(node);

+  }

+

+  fireSelectedNodeChanged(node: Node) {

+    this.selectedNodeChanged.emit(node);

+  }

+

+  setSelectedItem(item: MDMItem) {

+    this.nodeService.getNodeFromItem(item)

+        .subscribe(node => this.setSelectedNode(node));

+  }

+

+  getSelectedNode(): Node {

+    return this.selectedNode;

+  }

+

+  fireOnOpenInTree(items: MDMItem[]) {

+    this.onOpenInTree.emit(items);

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.service.ts
new file mode 100644
index 0000000..3aaa871
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.service.ts
@@ -0,0 +1,140 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Injectable, EventEmitter} from '@angular/core';
+import {Http, Response, Headers, RequestOptions} from '@angular/http';
+import {Observable} from 'rxjs/Observable';
+
+import {MDMItem} from '../core/mdm-item';
+import {Node} from './node';
+import {PropertyService} from '../core/property.service';
+import {PreferenceService, Preference} from '../core/preference.service';
+import {QueryService, Query} from '../tableview/query.service';
+import {plainToClass} from 'class-transformer';
+
+@Injectable()
+export class NodeService {
+
+  private _nodeUrl: string;
+
+  static mapSourceNameToName(environments: Node[], sourceName: string) {
+    let env = environments.find(n => n.sourceName === sourceName);
+    return env ? env.name : sourceName;
+  }
+
+  constructor(private http: Http,
+              private _prop: PropertyService,
+              private queryService: QueryService,
+              private preferenceService: PreferenceService) {
+      this._nodeUrl = _prop.getUrl('/mdm/environments');
+  }
+
+  searchNodes(query, env, type) {
+    return this.http.get(this._nodeUrl + '/' + env + '/' + type + '?' + query)
+              .map(res => plainToClass(Node, res.json().data))
+              .catch(this.handleError);
+  }
+
+  searchFT(query, env) {
+    return this.http.get(this._nodeUrl + '/' + env + '/search?q=' + query)
+              .map(res => plainToClass(Node, res.json().data))
+              .catch(this.handleError);
+  }
+
+  getNodes(node?: Node) {
+    if (node === undefined) {
+      return this.getRootNodes();
+    }
+    return this.getNode(this.getUrl(node));
+  }
+
+  addNode (name: string): Observable<Node> {
+    let body = JSON.stringify({ name });
+    let headers = new Headers({ 'Content-Type': 'application/json' });
+    let options = new RequestOptions({ headers: headers });
+
+    return this.http.post(this._nodeUrl, body, options)
+                    .map(res => plainToClass(Node, res.json().data))
+                    .catch(this.handleError);
+  }
+
+  deleteNode(node: Node) {
+    return this.http.delete(this.getUrl(node))
+      .map(res => plainToClass(Node, res.json().data))
+      .catch(this.handleError);
+  }
+
+  compareNode(node1, node2) {
+    if (node1 === undefined || node2 === undefined) { return; }
+    let n1 = node1.name + node1.id + node1.type + node1.sourceName;
+    let n2 = node2.name + node2.id + node2.type + node2.sourceName;
+    if (n1 === n2) { return true; };
+    return false;
+  }
+
+  getRootNodes() {
+    return this.http.get(this._nodeUrl)
+      .map(res => plainToClass(Node, res.json().data))
+      .catch((error: any) => Observable.throw(error.json().error || 'Server error'));
+  }
+
+  getNodeFromItem(mdmItem: MDMItem) {
+    if (mdmItem.type === 'Environment') {
+      return this.getNode(this._nodeUrl + '/' + mdmItem.source)
+        .map(nodes => (nodes && nodes.length > 0) ? nodes[0] : undefined);
+    } else {
+      return this.getNode(this._nodeUrl + '/' + mdmItem.source + '/' + this.typeToUrl(mdmItem.type) + '/' + mdmItem.id)
+        .map(nodes => (nodes && nodes.length > 0) ? nodes[0] : undefined);
+    }
+  }
+
+  getNodesFromItem(mdmItem: MDMItem) {
+    return this.getNode(this._nodeUrl + '/' + mdmItem.source + '/' + this.typeToUrl(mdmItem.type) + '/' + mdmItem.id)
+      .map(nodes => (nodes && nodes.length > 0) ? nodes[0] : undefined);
+  }
+
+  typeToUrl(type: string) {
+    switch (type) {
+      case 'StructureLevel':
+        return 'pools';
+      case 'MeaResult':
+        return 'measurements';
+      case 'SubMatrix':
+        return 'channelgroups';
+      case 'MeaQuantity':
+        return 'channels';
+      default:
+        return type.toLowerCase() + 's';
+    }
+  }
+
+  getNode(url: string) {
+    return this.http.get(url)
+      .map(res => plainToClass(Node, res.json().data));
+  }
+
+  getNodesByUrl(url: string) {
+    return this.http.get(this._nodeUrl + url)
+      .map(res => plainToClass(Node, res.json().data));
+  }
+
+  private getUrl(node: Node) {
+    return this._nodeUrl + '/' + node.sourceName + '/' + node.type + '/' + node.id;
+  }
+
+  private handleError(error: Response) {
+    console.error(error);
+    return Observable.throw(error.json().error || 'Server error');
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.ts
new file mode 100644
index 0000000..0eb290f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.ts
@@ -0,0 +1,48 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Type, Exclude, plainToClass, serialize, deserializeArray} from 'class-transformer';
+
+export class Node {
+  name: string;
+  id: number;
+  type: string;
+  sourceType: string;
+  sourceName: string;
+  @Type(() => Attribute)
+  attributes: Attribute[];
+  active: boolean;
+
+  getClass() {
+    switch (this.type) {
+      case 'StructureLevel':
+        return 'pool';
+      case 'MeaResult':
+        return 'measurement';
+      case 'SubMatrix':
+        return 'channelgroup';
+      case 'MeaQuantity':
+        return 'channel';
+      default:
+        return this.type.toLowerCase();
+    }
+  }
+}
+
+export class Attribute {
+  name: string;
+  value: string;
+  unit: string;
+  dataType: string;
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider.service.spec.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider.service.spec.ts
new file mode 100644
index 0000000..9baa5e9
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider.service.spec.ts
@@ -0,0 +1,77 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { DebugElement } from '@angular/core';

+import { By } from '@angular/platform-browser';

+import { Observable } from 'rxjs/Observable';

+

+import { ComponentFixture, TestBed, inject } from '@angular/core/testing';

+

+import { HttpModule } from '@angular/http';

+import { PropertyService } from '../core/property.service';

+import { NodeproviderService } from './nodeprovider.service';

+import {NodeService} from '../navigator/node.service';

+import {PreferenceService, Preference, Scope} from '../core/preference.service';

+import {MDMNotificationService} from '../core/mdm-notification.service';

+import { QueryService } from '../tableview/query.service';

+import {MDMItem} from '../core/mdm-item';

+

+declare function require(path: string): any;

+const defaultNodeProvider = require('../navigator/defaultnodeprovider.json');

+

+class TestPreferenceService {

+  getPreferenceForScope( scope: string, key?: string ): Observable<Preference[]> {

+    let p = new Preference();

+    p.value = JSON.stringify(defaultNodeProvider);

+    return Observable.of([p]);

+  }

+}

+

+describe ( 'NodeproviderService', () => {

+

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        PropertyService,

+        {

+          provide: PreferenceService,

+          useClass: TestPreferenceService

+        },

+        NodeproviderService,

+        MDMNotificationService,

+        QueryService,

+        NodeService]

+    });

+  });

+

+  it('getSubNodeprovider', inject([NodeproviderService], (nodeproviderService) => {

+      let item = new MDMItem('MDMNVH', 'Project', 1);

+      let query = nodeproviderService.getSubNodeprovider(item);

+

+      expect(query).toEqual('/pools?filter=Project.Id eq {Project.Id}');

+  }));

+

+  it('getSubNodeprovider not found', inject([NodeproviderService], (nodeproviderService) => {

+      let item = new MDMItem('MDMNVH', 'xxx', 1);

+      let query = nodeproviderService.getSubNodeprovider(item, defaultNodeProvider);

+

+      expect(query).toEqual(undefined);

+  }));

+

+  it('replace', inject([NodeproviderService], (nodeproviderService) => {

+      let item = new MDMItem('MDMNVH', 'Project', 1);

+      let query = nodeproviderService.replace('/pools?filter=Project.Id eq {Project.Id}', item);

+

+      expect(query).toEqual('/MDMNVH/pools?filter=Project.Id eq 1');

+  }));

+});

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider.service.ts
new file mode 100644
index 0000000..4b63dfa
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider.service.ts
@@ -0,0 +1,109 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import {Injectable, EventEmitter} from '@angular/core';

+import {Http, Response, Headers, RequestOptions} from '@angular/http';

+import {Observable} from 'rxjs/Observable';

+

+import {MDMNotificationService} from '../core/mdm-notification.service';

+import {PreferenceService, Preference, Scope} from '../core/preference.service';

+import {QueryService, Query} from '../tableview/query.service';

+import {PropertyService} from '../core/property.service';

+import {MDMItem} from '../core/mdm-item';

+

+declare function require(path: string): any;

+const defaultNodeProvider = require('./defaultnodeprovider.json');

+

+@Injectable()

+export class NodeproviderService {

+

+  public nodeProviderChanged: EventEmitter<any> = new EventEmitter<any>();

+  private nodeproviders = [defaultNodeProvider];

+

+  private activeNodeprovider: any = this.nodeproviders[0];

+

+  constructor(private http: Http,

+              private _prop: PropertyService,

+              private queryService: QueryService,

+              private preferenceService: PreferenceService,

+              private notificationService: MDMNotificationService) {

+      this.loadNodeproviders();

+  }

+

+  setActiveNodeprovider(nodeprovider: any) {

+    this.activeNodeprovider = nodeprovider;

+    this.nodeProviderChanged.emit(this.activeNodeprovider);

+  }

+

+  getActiveNodeprovider() {

+    return this.activeNodeprovider;

+  }

+

+  getNodeproviders() {

+    return this.nodeproviders;

+  }

+

+  loadNodeproviders() {

+    this.preferenceService.getPreferenceForScope(Scope.SYSTEM, 'nodeprovider.')

+      .subscribe( preferences => preferences.forEach(p => {

+          try {

+              this.nodeproviders.push(JSON.parse(p.value));

+          } catch (e) {

+            this.notificationService.notifyError('Die Einstellungen des Nodeproviders sind fehlerhaft.', e);

+          }

+      }));

+  }

+

+  getQueryForChildren(item: MDMItem) {

+    return this.replace(this.getSubNodeprovider(item), item);

+  }

+

+  getSubNodeprovider(item: MDMItem) {

+    let current = this.activeNodeprovider;

+    do {

+      if (current.type === item.type && current.children) {

+        return current.children.query;

+      } else {

+        current = current.children;

+      }

+    }

+    while (current);

+

+    return current;

+  }

+

+replace(query: string, item: MDMItem) {

+    return '/' + item.source + query.replace(/{(\w+)\.(\w+)}/g, function(match, type, attr) {

+

+      if (type !== item.type) {

+        this.notificationService.notifyWarn('Typ ' + type + ' wird nicht unterstützt! Es sollte Typ ' + item.type + ' verwendet werden.');

+      }

+

+      if (attr === 'Id') {

+        return '' + item.id;

+      }

+    });

+}

+

+  getPathTypes(type: string): string[] {

+    let current = this.activeNodeprovider;

+    let ancestorList: string[] = [];

+    while (current) {

+        ancestorList.push(current.type);

+        if (current.type === type) {

+          return ancestorList;

+        }

+        current = current.children;

+   }

+    return [];

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider2.json b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider2.json
new file mode 100644
index 0000000..dc36dee
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider2.json
@@ -0,0 +1,14 @@
+{

+  "name" : "Channels",

+  "type" : "Environment",

+  "children" : {

+    "type" : "Test",

+    "attribute" : "Name",

+    "query" : "/tests",

+    "children" : {

+      "type" : "Channel",

+      "attribute" : "Name",

+      "query" : "/channels?filter=Test.Id eq {Test.Id}"

+    }

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/edit-searchFields.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/search/edit-searchFields.component.html
new file mode 100644
index 0000000..0c6cf59
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/edit-searchFields.component.html
@@ -0,0 +1,99 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<style>

+  .modal-body >>> .dropup .dropdown-menu {

+    margin-bottom: 38px !important;

+  }

+

+  .modal-body >>> .ui-tree .ui-tree-container {

+    margin: 0 0 4px 0 !important;

+    width: 100%;

+    overflow: visible;

+  }

+

+  .modal-body >>> .ui-tree {

+    min-height: 50vh !important;

+    max-height: 76vh !important;

+    overflow: auto;

+  }

+

+  .modal-body >>> .ui-tree .ui-treenode .ui-treenode-content .ui-treenode-label {

+    padding-right: .75em;

+  }

+</style>

+<div bsModal #lgEditSearchFieldsModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="SelectSearchComponents" aria-hidden="true">

+  <div class="modal-dialog modal-lg">

+    <div class="modal-content">

+      <div class="modal-header">

+        <button type="button" title="{{TtlClose}}" class="close" (click)="childModal.hide()" aria-label="Close">

+          <span aria-hidden="true">&times;</span>

+        </button>

+        <h4 class="modal-title">{{LblSearchFieldEditor}}</h4>

+      </div>

+      <div class="modal-body">

+        <div class="container-fluid">

+          <div class="row">

+            <div class="col-md-4" style="min-height: 50vh; max-height: 77vh;">

+              <input [(ngModel)]="selectedSearchAttribute" [typeahead]="typeAheadValues"

+                  (typeaheadOnSelect)="typeaheadOnSelect($event)"

+                  typeaheadOptionField="label"

+                  typeaheadGroupField="group"

+                  typeaheadOptionsLimit="15"

+                  placeholder="Suchtext eingeben"

+                  class="form-control"

+                  style="margin-bottom: 15px;">

+              <searchattribute-tree [searchAttributes]="searchAttributes" [environments]="environments"></searchattribute-tree>

+            </div>

+            <div class="col-md-8" style="min-height: 50vh; max-height: 77vh; overflow-y:auto;">
+              <label for="search-source" class="control-label" style="padding: 8px 0 15px 0;">{{LblSelectedSearchAttributes}}</label>
+              <div *ngIf="layout.getSourceNames().length === 0" style="text-align: center;">

+                Keine Suchattribute ausgewählt.

+              </div>

+              <div *ngFor="let env of layout.getSourceNames()">

+              <div *ngIf="layout.getConditions(env).length > 0">

+                <span style="font-weight: bold;">{{mapSourceNameToName(env)}}</span>

+                <table class="table table-bordered searchdefinition">

+                  <colgroup>

+                    <col style="width: 44%;">

+                    <col style="width: 44%;">

+                    <col style="width: 4%;">

+                    <col style="width: 4%;">

+                    <col style="width: 4%;">

+                  </colgroup>

+                  <tr *ngFor="let condition of layout.getConditions(env)">

+                    <td>{{condition?.type}}</td>

+                    <td>{{condition?.attribute}}</td>

+                    <td style="width: 30px; text-align: center; vertical-align: middle;">

+                      <span class="glyphicon glyphicon-menu-up" [ngStyle]="{'visibility': isFirst(condition) ? 'hidden': 'visible'}" style="cursor: pointer; margin-bottom: 0;" (click)="moveUp(condition)"></span>

+                    </td>

+                    <td style="width: 30px; text-align: center; vertical-align: middle;">

+                      <span class="glyphicon glyphicon-menu-down" [ngStyle]="{'visibility': isLast(condition)? 'hidden': 'visible'}" style="cursor: pointer;" (click)="moveDown(condition)"></span>

+                    </td>

+                    <td style="text-align: center;"><span class="glyphicon glyphicon-remove" style="cursor: pointer" title="{{TtlRemove}}" (click)="removeCondition(condition)"></span></td>

+                  </tr>

+                </table>

+              </div>
+            </div>
+          </div>

+        </div>

+        <div class="row" style="margin-top: 15px;">

+          <div class="col-md-12">
+            <button type="button" class="btn btn-default pull-right" (click)="addSearchFields()" [disabled]="conditions.length === 0">

+              <span class="glyphicon glyphicon-ok"></span> {{LblApplyChanges}}

+            </button>

+          </div>

+        </div>

+      </div>

+    </div>

+  </div>

+</div>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/edit-searchFields.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/edit-searchFields.component.ts
new file mode 100644
index 0000000..6606eef
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/edit-searchFields.component.ts
@@ -0,0 +1,207 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import {Component, ViewChild, Input, Output, EventEmitter, OnChanges, SimpleChanges, OnInit} from '@angular/core';

+

+import {SearchDefinition, SearchAttribute, SearchLayout} from './search.service';

+import {FilterService, SearchFilter, Condition, Operator} from './filter.service';

+import {NodeService} from '../navigator/node.service';

+

+import {Node} from '../navigator/node';

+import {MDMItem} from '../core/mdm-item';

+import {SearchattributeTreeComponent} from '../searchattribute-tree/searchattribute-tree.component';

+

+import {IDropdownItem, IMultiselectConfig} from 'ng2-dropdown-multiselect';

+import {TypeaheadMatch} from 'ng2-bootstrap/typeahead';

+import {ModalDirective} from 'ng2-bootstrap';

+import {TreeNode} from 'primeng/primeng';

+import {MDMNotificationService} from '../core/mdm-notification.service';

+import {classToClass} from 'class-transformer';

+

+export class SearchField {

+  group: string;

+  attribute: string;

+

+  constructor(group?: string, attribute?: string) {

+    this.group = group || '';

+    this.attribute = attribute || '';

+  }

+

+  equals(searchField: SearchField) {

+    return (searchField.group === this.group && searchField.attribute === this.attribute);

+  }

+}

+

+@Component({

+  selector: 'edit-searchFields',

+  templateUrl: 'edit-searchFields.component.html',

+})

+export class EditSearchFieldsComponent implements OnChanges, OnInit {

+

+  readonly LblApplyChanges = 'Änderungen übernehmen';

+  readonly LblSearchFieldEditor = 'Suchfeld Editor';

+  readonly LblSelectedSearchAttributes = 'Ausgewählte Suchattribute';

+  readonly TtlClose = 'Schließen';

+  readonly TtlRemove = 'Entfernen';

+

+  @ViewChild('lgEditSearchFieldsModal') public childModal: ModalDirective;

+  @ViewChild(SearchattributeTreeComponent) tree: SearchattributeTreeComponent;

+

+  @Input() environments: Node[];

+  @Input() searchAttributes: { [env: string]: SearchAttribute[] } = {};

+  typeAheadValues: { label: string, group: string, attribute: SearchAttribute }[] = [];

+

+  @Output()

+  conditionsSubmitted = new EventEmitter<Condition[]>();

+

+  layout: SearchLayout = new SearchLayout;

+  conditions: Condition[] = [];

+  selectedSearchAttribute: SearchAttribute;

+

+  subscription: any;

+

+  constructor(private filterService: FilterService,

+    private notificationService: MDMNotificationService) { }

+

+  ngOnInit() {

+      this.tree.onNodeSelect$.subscribe(node => this.nodeSelect(node));

+  }

+

+  ngOnChanges(changes: SimpleChanges) {

+    if (changes['searchAttributes'] || changes['environments']) {

+      let ar = Object.keys(this.searchAttributes)

+          .map(env => this.searchAttributes[env])

+          .reduce((acc, value) => acc.concat(value), <SearchAttribute[]> [])

+          .map(sa => { return { 'label': sa.boType + '.' + sa.attrName, 'group': sa.boType, 'attribute': sa }; });

+

+      this.typeAheadValues = this.uniqueBy(ar, p => p.label);

+    }

+    if (changes['environments']) {

+      this.conditionUpdated();

+    }

+  }

+

+  show(conditions?: Condition[]) {

+    if (conditions) {

+      this.conditions = classToClass(conditions);

+    } else {

+      this.conditions = [];

+    }

+    this.conditionUpdated();

+    this.childModal.show();

+    return this.conditionsSubmitted;

+  }

+

+  conditionUpdated() {

+    let envs = (this.environments || []).map(node => node.sourceName);

+    this.layout = SearchLayout.createSearchLayout(envs, this.searchAttributes, this.conditions);

+  }

+

+  nodeSelect(node: TreeNode) {

+    if (node && node.type === 'attribute') {

+      let sa = <SearchAttribute>node.data;

+      this.pushCondition(new Condition(sa.boType, sa.attrName, Operator.EQUALS, [], sa.valueType));

+      this.conditionUpdated();

+    }

+  }

+

+  removeCondition(condition: Condition) {

+    let index = this.conditions.findIndex(c => condition.type === c.type && condition.attribute === c.attribute);

+    this.conditions.splice(index, 1);

+    this.conditionUpdated();

+  }

+

+  addSearchFields() {

+    this.childModal.hide();

+    this.conditionsSubmitted.emit(this.conditions);

+  }

+

+  public typeaheadOnSelect(match: TypeaheadMatch) {

+    this.pushCondition(new Condition(match.item.attribute.boType,

+      match.item.attribute.attrName, Operator.EQUALS, [], match.item.attribute.valueType));

+    this.conditionUpdated();

+    this.selectedSearchAttribute = undefined;

+  }

+

+  pushCondition(condition: Condition) {

+    if (this.conditions.find(c => condition.type === c.type && condition.attribute === c.attribute)) {

+      this.notificationService.notifyInfo('Das Suchfeld wurde bereits ausgewählt!', 'Info');

+    } else {

+      this.conditions.push(condition);

+      this.conditionUpdated();

+    }

+  }

+

+  mapSourceNameToName(sourceName: string) {

+    return NodeService.mapSourceNameToName(this.environments, sourceName);

+  }

+

+  isLast(col: Condition) {

+    let sourceName = this.layout.getSourceName(col);

+    if (sourceName) {

+      let conditionsInSameSource = this.layout.getConditions(sourceName);

+      return conditionsInSameSource.indexOf(col) === conditionsInSameSource.length - 1;

+    }

+  }

+

+  isFirst(col: Condition) {

+    let sourceName = this.layout.getSourceName(col);

+    if (sourceName) {

+      let conditionsInSameSource = this.layout.getConditions(sourceName);

+      return conditionsInSameSource.indexOf(col) === 0;

+    }

+  }

+

+  moveUp(condition: Condition) {

+    if (!this.isFirst(condition)) {

+      let sourceName = this.layout.getSourceName(condition);

+      if (sourceName) {

+        let conditionsInSameSource = this.layout.getConditions(sourceName);

+

+        let oldIndex = conditionsInSameSource.indexOf(condition);

+        let otherCondition = conditionsInSameSource[oldIndex - 1];

+        this.swap(condition, otherCondition);

+      }

+    }

+  }

+

+  moveDown(condition: Condition) {

+    if (!this.isLast(condition)) {

+      let sourceName = this.layout.getSourceName(condition);

+      if (sourceName) {

+        let conditionsInSameSource = this.layout.getConditions(sourceName);

+

+        let oldIndex = conditionsInSameSource.indexOf(condition);

+        let otherCondition = conditionsInSameSource[oldIndex + 1];

+        this.swap(condition, otherCondition);

+      }

+    }

+  }

+

+  private swap(condition1: Condition, condition2: Condition) {

+    let index1 = this.conditions.findIndex(c => c.type === condition1.type && c.attribute === condition1.attribute);

+    let index2 = this.conditions.findIndex(c => c.type === condition2.type && c.attribute === condition2.attribute);

+

+    let tmp = this.conditions[index1];

+    this.conditions[index1] = this.conditions[index2];

+    this.conditions[index2] = tmp;

+    this.conditionUpdated();

+  }

+

+  private uniqueBy<T>(a: T[], key: (T) => any) {

+    let seen = {};

+    return a.filter(function(item) {

+      let k = key(item);

+      return seen.hasOwnProperty(k) ? false : (seen[k] = true);

+    });

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/filter.service.spec.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/filter.service.spec.ts
new file mode 100644
index 0000000..d171186
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/filter.service.spec.ts
@@ -0,0 +1,127 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { BaseRequestOptions, Http, HttpModule, Response, ResponseOptions } from '@angular/http';

+import { Observable } from 'rxjs/Observable';

+import { TestBed, async, inject } from '@angular/core/testing';

+import {PreferenceService, Preference, Scope} from '../core/preference.service';

+import {PropertyService} from '../core/property.service';

+

+import {Condition, Operator, OperatorUtil, FilterService, SearchFilter} from './filter.service';

+

+class TestPreferenceService {

+  getPreferenceForScope(scope: string, key?: string): Observable<Preference[]> {

+    return Observable.of([

+    {

+      id: 1,

+      key: 'filter.nodes.test',

+      scope: Scope.USER,

+      source: null,

+      user: 'testUser',

+      value: '{"conditions":[],"name":"TestFilter","environments":[],"resultType":"Test","fulltextQuery":""}'

+    }

+  ]);

+  }

+}

+

+describe('OperatorUtil', () => {

+

+  describe('toString()', () => {

+

+    it('should return associated string', () => {

+      expect(OperatorUtil.toString(Operator.EQUALS)).toMatch('=');

+      expect(OperatorUtil.toString(Operator.LESS_THAN)).toMatch('<');

+      expect(OperatorUtil.toString(Operator.GREATER_THAN)).toMatch('>');

+      expect(OperatorUtil.toString(Operator.LIKE)).toMatch('like');

+    });

+  });

+

+  describe('toFilterString()', () => {

+    it('should return associated filterstring', () => {

+      expect(OperatorUtil.toFilterString(Operator.EQUALS)).toMatch('eq');

+      expect(OperatorUtil.toFilterString(Operator.LESS_THAN)).toMatch('lt');

+      expect(OperatorUtil.toFilterString(Operator.GREATER_THAN)).toMatch('gt');

+      expect(OperatorUtil.toFilterString(Operator.LIKE)).toMatch('lk');

+    });

+  });

+});

+

+describe('FilterService', () => {

+

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        FilterService,

+        PropertyService,

+        {

+          provide: PreferenceService,

+          useClass: TestPreferenceService

+        },

+      ]

+    });

+

+  });

+

+  describe('setSelectedFilter()', () => {

+

+    it('should emit new no-filter', async(inject([FilterService], (service) => {

+      let filter = new SearchFilter(service.NO_FILTER_NAME, [], 'Test', '', []);

+      spyOn(service.filterChanged$ , 'emit');

+

+      service.setSelectedFilter(undefined);

+

+      expect(service.filterChanged$.emit).toHaveBeenCalledWith(filter);

+    })));

+  });

+

+  describe('setSelectedFilter(filter)', () => {

+

+    it('should emit filter', async(inject([FilterService], (service) => {

+      let filter = new SearchFilter('TestFilter', [], 'Test', '', []);

+      spyOn(service.filterChanged$ , 'emit');

+

+      service.setSelectedFilter(filter);

+

+      expect(service.filterChanged$.emit).toHaveBeenCalledWith(filter);

+    })));

+  });

+

+  describe('getFilters()', () => {

+

+    it('should return array of filters from preference',  async(inject([FilterService], (service) => {

+      let filters = [new SearchFilter('TestFilter', [], 'Test', '', [])];

+

+      service.getFilters().subscribe(f => expect(f).toEqual(filters));

+    })));

+  });

+

+  describe('filterToPreference()', () => {

+

+    it('should return preference holding input filter',  async(inject([FilterService], (service) => {

+      let filter = new SearchFilter('TestFilter', [], 'Test', '', []);

+      let pref = service.filterToPreference(filter);

+

+      expect(pref.scope).toEqual(Scope.USER);

+      expect(pref.key).toMatch('filter.nodes.TestFilter');

+      expect(pref.value).toEqual('{"conditions":[],"name":"TestFilter","environments":[],"resultType":"Test","fulltextQuery":""}');

+    })));

+

+    it('should return preference holding input filter',  async(inject([FilterService], (service) => {

+      let pref = service.filterToPreference(undefined);

+

+      expect(pref.scope).toEqual(Scope.USER);

+      expect(pref.key).toMatch('filter.nodes.');

+      expect(pref.value).toEqual(undefined);

+    })));

+  });

+});

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/filter.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/filter.service.ts
new file mode 100644
index 0000000..8150814
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/filter.service.ts
@@ -0,0 +1,154 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import {Injectable, EventEmitter} from '@angular/core';

+import {Http, Response, Headers, RequestOptions} from '@angular/http';

+import {Observable} from 'rxjs/Observable';

+

+import {SearchBase} from './search-base';

+import {TextboxSearch} from './search-textbox';

+import {DropdownSearch} from './search-dropdown';

+

+import {SearchService} from './search.service';

+import {PropertyService} from '../core/property.service';

+import {Node} from '../navigator/node';

+import {SearchAttribute} from './search.service';

+import {QueryService, Query, SearchResult, Filter} from '../tableview/query.service';

+import {View} from '../tableview/tableview.service';

+import {PreferenceService, Preference, Scope} from '../core/preference.service';

+import {Type, Exclude, plainToClass, serialize, deserialize} from 'class-transformer';

+

+export enum Operator {

+  EQUALS,

+  LESS_THAN,

+  GREATER_THAN,

+  LIKE

+}

+

+export namespace OperatorUtil {

+  export function values() {

+    return [ Operator.EQUALS, Operator.LESS_THAN, Operator.GREATER_THAN, Operator.LIKE ];

+  }

+  export function toString(operator: Operator) {

+      switch (operator) {

+          case Operator.EQUALS:

+            return '=';

+          case Operator.LESS_THAN:

+            return '<';

+          case Operator.GREATER_THAN:

+            return '>';

+          case Operator.LIKE:

+            return 'like';

+          default:

+            return undefined;

+      }

+  }

+  export function toFilterString(operator: Operator) {

+      switch (operator) {

+          case Operator.EQUALS:

+            return 'eq';

+          case Operator.LESS_THAN:

+            return 'lt';

+          case Operator.GREATER_THAN:

+            return 'gt';

+          case Operator.LIKE:

+            return 'lk';

+          default:

+            return undefined;

+      }

+  }

+}

+

+export class Condition {

+  type: string;

+  attribute: string;

+  operator: Operator;

+  value: string[];

+  valueType: string;

+

+  @Exclude()

+  sortIndex: number;

+

+  constructor(type: string, attribute: string, operator: Operator, value: string[], valueType?: string) {

+    this.type = type;

+    this.attribute = attribute;

+    this.operator = operator;

+    this.value = value;

+    if (valueType) {

+      this.valueType = valueType.toLowerCase();

+    } else {

+      this.valueType = 'string';

+    }

+  }

+}

+

+export class SearchFilter {

+  name: string;

+  environments: string[];

+  resultType: string;

+  fulltextQuery: string;

+  conditions: Condition[] = [];

+

+  constructor(name: string, environments: string[], resultType: string, fulltextQuery: string, conditions: Condition[]) {

+    this.name = name;

+    this.environments = environments;

+    this.resultType = resultType;

+    this.fulltextQuery = fulltextQuery;

+    this.conditions = conditions;

+  }

+}

+

+@Injectable()

+export class FilterService {

+  public readonly NO_FILTER_NAME = 'Kein Filter ausgewählt';

+  public readonly NEW_FILTER_NAME = 'Neuer Filter';

+  public currentFilter = new SearchFilter(this.NO_FILTER_NAME, [], 'Test', '', []);

+  public filterChanged$ = new EventEmitter<SearchFilter>();

+

+  constructor(private http: Http,

+              private _prop: PropertyService,

+              private preferenceService: PreferenceService) {

+  }

+

+  setSelectedFilter(filter: SearchFilter) {

+    if (filter) {

+      this.filterChanged$.emit(filter);

+    } else {

+      this.filterChanged$.emit(new SearchFilter(this.NO_FILTER_NAME, [], 'Test', '', []));

+    }

+  }

+

+  getFilters() {

+    return this.preferenceService.getPreferenceForScope(Scope.USER, 'filter.nodes.')

+      .map(preferences => preferences.map(p => this.preferenceToFilter(p)));

+  }

+

+  saveFilter(filter: SearchFilter) {

+    return this.preferenceService.savePreference(this.filterToPreference(filter));

+  }

+

+  private preferenceToFilter(pref: Preference) {

+    return deserialize(SearchFilter, pref.value);

+  }

+

+  private filterToPreference(filter: SearchFilter) {

+    let pref = new Preference();

+    pref.value = serialize(filter);

+    pref.key = filter ? 'filter.nodes.' + filter.name : 'filter.nodes.';

+    pref.scope = Scope.USER;

+    return pref;

+  }

+

+  deleteFilter(name: string) {

+    return this.preferenceService.deletePreferenceByScopeAndKey(Scope.USER, 'filter.nodes.' + name);

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.component.html
new file mode 100644
index 0000000..f2115c6
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.component.html
@@ -0,0 +1,238 @@
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
+
+<style>
+   :host /deep/ .dropdown {
+    width: 200px;
+  }
+
+   :host /deep/ .dropdown-toggle {
+    overflow: hidden;
+    padding-right: 24px/* Optional for caret */
+    ;
+    text-align: left;
+    text-overflow: ellipsis;
+    width: 100%;
+  }
+  /* Optional for caret */
+
+   :host /deep/ .dropdown-toggle .caret {
+    position: absolute;
+    right: 12px;
+    top: calc(50% - 2px);
+  }
+
+  #searchtext .form-control, #searchtext .btn {
+      border-color: #000000;
+      outline: 0;
+      box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(0, 0, 0,.6);
+  }
+</style>
+<form class="form-inline" style="margin-bottom: 10px; width:100% !important;">
+  <div class="form-group">
+    <label for="search-source" class="control-label">{{LblFilter}}</label>
+    <div class="btn-group" dropdown>
+      <button id="search-source" title="{{TtlSelectFilter}}" type="button" class="btn btn-default dropdown-toggle" dropdownToggle aria-haspopup="true" aria-expanded="false">
+        {{currentFilter.name}} <span class="caret"></span>
+      </button>
+      <ul class="dropdown-menu" dropdownMenu>
+        <li *ngFor="let filter of filters">
+          <a class="dropdown-item" (click)="selectFilter(filter)">{{filter.name}}</a>
+        </li>
+      </ul>
+    </div>
+  </div>
+  <div class="form-group">
+    <label for="search-source" class="control-label">{{LblSource}}</label>
+    <dropdown-multiselect name="envs" title="{{TtlSelectSource}}" [dropdownConfig]="dropdownConfig" [(ngModel)]="dropdownModel" (ngModelChange)="selectedEnvironmentsChanged($event)"></dropdown-multiselect>
+  </div>
+  <div class="form-group">
+    <label for="search-resulttype" class="control-label">{{LblResultType}}</label>
+    <div class="btn-group" dropdown>
+      <button id="search-resulttype" title="{{TtlSelectResultType}}" type="button" class="btn btn-default dropdown-toggle" dropdownToggle aria-haspopup="true" aria-expanded="false">
+        {{getSearchDefinition(currentFilter.resultType)?.label}} <span class="caret"></span>
+      </button>
+      <ul class="dropdown-menu" dropdownMenu>
+        <li *ngFor="let def of definitions" role="menuitem">
+          <a class="dropdown-item" (click)="selectResultType(def)">{{def.label}}</a>
+        </li>
+      </ul>
+    </div>
+  </div>
+</form>
+
+<div class="row" style="margin-bottom: 10px;">
+  <div class="col-lg-2"></div>
+  <div class="col-lg-8">
+    <div class="input-group" id="searchtext" >
+      <input type="text" class="form-control" name="searchText" placeholder="Suchtext eingeben" [(ngModel)]="currentFilter.fulltextQuery" (keyup.enter)="onSearch()" aria-describedby="basic-addon1" style="width:100%"
+      title="M&ouml;gliche Operatoren:
+        + steht f&uuml;r einen UND Operator
+        | steht f&uuml;r einen ODER Operator
+        - negiert einen einzelnen Term
+        &quot; fasst einen Menge von Termen zu eine Phrase zusammen
+        * am Ende eines Suchterms beschreibt eine Pr&auml;fixsuche
+        ( und ) beschreibt die Operatorpr&auml;ferenz
+        ~N nach einem Wort beschreibt den Editierabstand (Unsch&auml;rfe)
+        ~N nach einer Phrase beschreibt den Wortabstand
+Wenn nach einem der Spezialsymbol gesucht werden soll, m&uuml;ssen dieses mit \ escaped werden.">
+      <span class="input-group-btn">
+        <button type="button" class="btn btn-default" (click)="onSearch()">{{LblSearch}}</button>
+      </span>
+    </div>
+  </div>
+  <div class="col-lg-2"></div>
+</div>
+
+<accordion>
+
+  <accordion-group [isOpen]="true" t="isAdvancedSearchOpen" #advancedSearch [ngClass]="{'greyed': !isBoxChecked}">
+    <div accordion-heading class="thinheader">
+      <div class="row">
+        <div class="col-xs-9">
+          <div class="pull-left">
+            <input type="checkbox" [(ngModel)]="isBoxChecked" (click)="onCheckBoxClick($event)" title="{{getAdvancedSearchCbxTitle()}}"/> &nbsp;{{LblAdvancedSearch}} &nbsp;&nbsp;
+            <button type="button" title="{{TtlNewSearchFields}}" class="btn btn-default" (click)="showSearchFieldsEditor($event)" [disabled]=!isBoxChecked><span class="glyphicon glyphicon-plus"></span></button>
+            <button type="button" title="{{TtlEditSearchFields}}" class="btn btn-default" (click)="showSearchFieldsEditor($event,currentFilter.conditions)" [disabled]=!isBoxChecked><span class="glyphicon glyphicon-edit"></span></button>
+            <button type="button" class="btn btn-default" (click)="showSaveModal($event)" title="{{TtlSaveSearchFilter}}" [disabled]=!isBoxChecked><span class="fa fa-floppy-o"></span></button>
+            <button type="button" class="btn btn-default" (click)="deleteFilter($event)" title="{{TtlDeleteFilter}}"><span class="glyphicon glyphicon-remove"></span></button>
+          </div>
+        </div>
+        <div class="col-xs-3 pull-right">
+          <div class="pull-right">
+            <button type="button" class="btn btn-default" (click)="resetConditions($event)" title="{{TtlResetSearchConditions}}" [disabled]=!isBoxChecked><span class="glyphicon glyphicon-erase"></span></button>
+            <span class="glyphicon" style="color: #333 !important; padding-left: 15px;" [ngClass]="{'glyphicon-chevron-down': advancedSearch?.isOpen, 'glyphicon-chevron-right': !advancedSearch?.isOpen}"></span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="container-fluid">
+      <div class="row">
+        <div class="col-md-12">
+          <div *ngIf="layout.getSourceNames().length === 0" style="text-align: center; margin-bottom: 5px;">
+            Keine Suchattribute ausgewählt.
+          </div>
+          <div *ngFor="let env of layout.getSourceNames()">
+            <div *ngIf="layout.getConditions(env).length > 0">
+              <div style="font-weight: bold;" *ngIf="mapSourceNameToName(env) === 'Global'" title="Globale Suchattribute sind Attribute, die in allen ausgewählten Datenquellen vorhanden sind. Global Suchattribute werden nur einmal dargestellt und die definierte Bedingung wird in allen Datenquellen angewendet.">
+                Globale Suchattribute
+              </div>
+              <div style="font-weight: bold;" *ngIf="mapSourceNameToName(env) !== 'Global'"  title="Suchattribute, die nicht in allen Datenquellen verfügbar sind, sondern nur in der aktuellen Datenquelle.">
+                Suchattribute aus {{mapSourceNameToName(env)}}
+              </div>
+              <table class="table table-bordered searchdefinition">
+                <colgroup>
+                  <col style="width: 17%;">
+                  <col style="width: 17%;">
+                  <col style="width: 5%;">
+                  <col style="width: 58%;">
+                  <col style="width: 3%;">
+                </colgroup>
+                <tr search-condition class="condition" *ngFor="let condition of layout.getConditions(env)"
+                    [env]="env"
+                    [condition]="condition"
+                    [disabled]="!isBoxChecked"
+                    [selectedEnvs]="selectedEnvironments"
+                    (onRemove)="removeCondition($event)">
+                </tr>
+              </table>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </accordion-group>
+  <accordion-group [isOpen]="isSearchResultsOpen" #searchResults>
+    <div accordion-heading class="thinheader">
+      <div class="row">
+        <div class="col-xs-9">
+          <div class="pull-left">
+            {{LblResults}} &nbsp;&nbsp;
+            <mdm-view (click)="onViewClick($event)"></mdm-view>
+          </div>
+        </div>
+        <div class="col-xs-3 pull-right">
+          <div class="pull-right">
+            <span class="badge">{{results?.rows.length}}</span>&nbsp;
+            <button type="button" class="btn btn-default" (click)="clearResultlist($event)" title="{{TtlClearSearchResults}}"><span class="glyphicon glyphicon-erase"></span></button>
+            <button type="button" class="btn btn-default" title="{{TtlSelectionToBasket}}" (click)="selected2Basket($event)">
+              <span class="glyphicon glyphicon-shopping-cart" style="color:#DADADA; margin-right: -12px;"></span>
+              <span class="glyphicon glyphicon-shopping-cart" style="color:#A8A4A4; margin-right: -12px;"></span>
+              <span class="glyphicon glyphicon-shopping-cart"></span>
+            </button>
+            <span class="glyphicon" [ngClass]="{'glyphicon-chevron-down': searchResults?.isOpen, 'glyphicon-chevron-right': !searchResults?.isOpen}" style="padding-left: 15px;"></span>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="container-fluid">
+      <div class="row">
+          <mdm-tableview [results]="results" [view]="viewComponent.selectedView" isShopable="true" [menuItems]="contextMenuItems" [selectedEnvs]="selectedEnvironments" [searchAttributes]="allSearchAttributesForCurrentResultType" [environments]="environments">
+          </mdm-tableview>
+      </div>
+    </div>
+  </accordion-group>
+</accordion>
+
+<div bsModal #lgSaveModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="SelectSearchComponents" aria-hidden="true">
+  <div class="modal-dialog modal-md">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" (click)="childSaveModal.hide()" aria-label="Close">
+          <span aria-hidden="true">&times;</span>
+        </button>
+        <h4 class="modal-title">{{LblSaveFilterAs}}:</h4>
+      </div>
+      <div class="modal-body">
+        <div class="container-fluid">
+          <div class="row" *ngIf="filters?.length > 0">
+            <p-dataTable
+              [value]="filters"
+              resizableColumns="false"
+              [reorderableColumns]="false"
+              [rows]="10"
+              [paginator]="true"
+              [pageLinks]="3"
+              [rowsPerPageOptions]="[10,20,50]"
+              [(selection)]="selectedRow"
+              (onRowClick)="onRowSelect($event)"
+              (onRowSelect)="onRowSelect($event)">
+              <p-column [style]="{'width':'30px'}" selectionMode="single"></p-column>
+              <p-column header="{{LblExistingFilterNames}}">
+                <template pTemplate="body" let-col let-row="rowData" >
+                  {{row.name}}
+                </template>
+              </p-column>
+            </p-dataTable>
+          </div>
+          <div class="row" style="margin-top: 15px;">
+            <div class="col-md-10" style="padding-left: 0;">
+            <input type="text" class="form-control" placeholder="Filtername" [(value)]="filterName" (input)="filterName = $event.target.value" (keyup.enter)="saveFilter($event)">
+            </div>
+            <div class="col-md-2" style="padding: 0;">
+              <form class="form-inline">
+                <button type="button" class="btn btn-default pull-right" (click)="saveFilter($event)" [disabled]="!filterName" title="{{getSaveFilterBtnTitle()}}">
+                  <span class="fa fa-floppy-o"></span> {{LblSave}}
+                </button>
+              </form>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<edit-searchFields [searchAttributes]="allSearchAttributesForCurrentResultType" [environments]="selectedEnvironments"></edit-searchFields>
+<overwrite-dialog></overwrite-dialog>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.component.ts
new file mode 100644
index 0000000..97f7a2a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.component.ts
@@ -0,0 +1,418 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Component, ViewChild, OnInit, Input, OnDestroy, OnChanges, SimpleChanges} from '@angular/core';
+import {Observable} from 'rxjs/Observable';
+
+import {SearchService, SearchDefinition, SearchAttribute, SearchLayout} from './search.service';
+import {DropdownSearch} from './search-dropdown';
+import {SearchBase} from './search-base';
+
+import {FilterService, SearchFilter, Condition, Operator} from './filter.service';
+import {NodeService} from '../navigator/node.service';
+import {BasketService} from '../basket/basket.service';
+import {QueryService, Query, SearchResult, Filter} from '../tableview/query.service';
+
+import {LocalizationService} from '../localization/localization.service';
+import {MDMNotificationService} from '../core/mdm-notification.service';
+
+import {Node} from '../navigator/node';
+import {MDMItem} from '../core/mdm-item';
+
+import {TableviewComponent} from '../tableview/tableview.component';
+import {ViewComponent} from '../tableview/view.component';
+
+import {View} from '../tableview/tableview.service';
+import {IDropdownItem, IMultiselectConfig} from 'ng2-dropdown-multiselect';
+import {TypeaheadMatch} from 'ng2-bootstrap/typeahead';
+
+import {ModalDirective} from 'ng2-bootstrap';
+
+import {TreeModule, TreeNode, DataTableModule, SharedModule, ContextMenuModule, MenuItem} from 'primeng/primeng';
+import {EditSearchFieldsComponent} from './edit-searchFields.component';
+import {OverwriteDialogComponent} from '../core/overwrite-dialog.component';
+
+import {classToClass} from 'class-transformer';
+
+
+@Component({
+  selector: 'mdm-search',
+  templateUrl: 'mdm-search.component.html',
+})
+export class MDMSearchComponent implements OnInit, OnDestroy {
+
+
+  readonly LblAdvancedSearch = 'Erweiterte Suche';
+  readonly LblExistingFilterNames = 'Vorhandene Suchfilter';
+  readonly LblFilter = 'Suchfilter';
+  readonly LblResultType = 'Ergebnistyp';
+  readonly LblResults = 'Ergebnisse';
+  readonly LblSave = 'Speichern';
+  readonly LblSaveFilterAs = 'Suchfilter speichern unter';
+  readonly LblSearch = 'Suche';
+  readonly LblSource = 'Quelle';
+  readonly TtlDeleteFilter = 'Suchfilter löschen';
+  readonly TtlDisableAdvancedSearch = 'Erweiterte Suche deaktivieren';
+  readonly TtlEditSearchFields = 'Suchfilter bearbeiten';
+  readonly TtlEnableAdvancedSearch = 'Erweiterte Suche aktivieren';
+  readonly TtlNewSearchFields = 'Neuen Suchfilter anlegen';
+  readonly TtlNoNameSet = 'Name nicht gesetzt!';
+  readonly TtlResetSearchConditions = 'Suchkriterien zurücksetzen';
+  readonly TtlSaveFilter = 'Suchfilter speichern';
+  readonly TtlSaveSearchFilter = 'Suchfilter speichern';
+  readonly TtlSelectionToBasket = 'Auswahl zum Warenkorb hinzufügen';
+  readonly TtlSelectFilter = 'Suchfilter auswählen';
+  readonly TtlSelectResultType = 'Ergebnisstyp auswählen';
+  readonly TtlSelectSource = 'Quellen auswählen';
+  readonly TtlClearSearchResults = 'Suchergebnisliste leeren';
+
+  maxResults = 1000;
+
+  filters: SearchFilter[] = [];
+  currentFilter: SearchFilter;
+  filterName = '';
+
+  environments: Node[];
+  selectedEnvironments: Node[];
+
+  definitions: SearchDefinition[];
+
+  results: SearchResult = new SearchResult();
+  allSearchAttributes: { [type: string]: { [env: string]: SearchAttribute[] }} = {};
+  allSearchAttributesForCurrentResultType: { [env: string]: SearchAttribute[] } = {};
+
+  isAdvancedSearchOpen = false;
+  isSearchResultsOpen = false;
+
+  layout: SearchLayout = new SearchLayout;
+  public dropdownModel: IDropdownItem[] = [];
+  public dropdownConfig: IMultiselectConfig = {
+      showCheckAll: false,
+      showUncheckAll: false,
+      checkClasses: ['fa', 'fa-fw', 'fa-check'],
+      uncheckClasses: ['fa', 'fa-fw', 'fa-times']
+    };
+
+  searchFields: { group: string, attribute: string }[] = [];
+
+  subscription: any;
+  isBoxChecked = true;
+  searchExecuted = false;
+
+  selectedRow: SearchFilter;
+  lazySelectedRow: SearchFilter;
+
+  contextMenuItems: MenuItem[] = [
+    { label: 'In Warenkorb legen', icon: 'glyphicon glyphicon-shopping-cart', command: (event) => this.addSelectionToBasket() }
+  ];
+
+  @ViewChild(ViewComponent)
+  viewComponent: ViewComponent;
+
+  @ViewChild(TableviewComponent)
+  private tableViewComponent: TableviewComponent;
+
+  @ViewChild('lgSaveModal')
+  childSaveModal: ModalDirective;
+
+  @ViewChild(EditSearchFieldsComponent)
+  private editSearchFieldsComponent: EditSearchFieldsComponent;
+
+  @ViewChild(OverwriteDialogComponent)
+  private overwriteDialogComponent: OverwriteDialogComponent;
+
+  constructor(private searchService: SearchService,
+    private queryService: QueryService,
+    private filterService: FilterService,
+    private nodeService: NodeService,
+    private localService: LocalizationService,
+    private notificationService: MDMNotificationService,
+    private basketService: BasketService) { }
+
+  ngOnInit() {
+    // Load contents for environment selection
+    this.nodeService.getNodes()
+      .do(envs => this.environments = envs)
+      .do(nodes => this.loadSearchAttributes(nodes.map(env => env.sourceName)))
+      .map(nodes => nodes.map(env => <IDropdownItem>{ id: env.sourceName, label: env.name, selected: true }))
+      .subscribe(
+      dropDownItems => {
+        this.dropdownModel = dropDownItems;
+        this.selectedEnvironmentsChanged(dropDownItems);
+      },
+      error => this.notificationService.notifyError('Datenquellen konnten nicht geladen werden!', error));
+
+    this.searchService.getDefinitionsSimple()
+      .subscribe(defs => this.definitions = defs);
+
+      this.loadFilters();
+
+    this.filterService.filterChanged$.subscribe(filter => this.onFilterChanged(filter));
+    this.viewComponent.viewChanged$.subscribe(() => this.onViewChanged());
+
+    this.selectFilter(this.filterService.currentFilter);
+  }
+
+  ngOnDestroy() {
+     this.filterService.currentFilter = this.currentFilter;
+  }
+
+  onViewClick(e: Event) {
+    e.stopPropagation();
+  }
+
+  onCheckBoxClick(event: any) {
+    event.stopPropagation();
+    this.isBoxChecked = event.target.checked;
+  }
+
+  onViewChanged() {
+    if (this.searchExecuted) {
+      this.onSearch();
+    }
+  }
+
+  selectedEnvironmentsChanged(items: IDropdownItem[]) {
+    this.currentFilter.environments = items.filter(item => item.selected).map(item => item.id);
+    if (this.environments) {
+      let envs = this.environments.filter(env =>
+        this.currentFilter.environments.find(envName => envName === env.sourceName));
+      if (envs.length === 0) {
+        this.dropdownModel.filter( item => item.id === this.selectedEnvironments[0].sourceName)[0]
+        .selected = true;
+      } else {
+        this.selectedEnvironments = envs;
+      }
+    }
+    this.calcCurrentSearch();
+  }
+
+  loadFilters() {
+    this.filters = [];
+    this.filterService.getFilters()
+      .defaultIfEmpty([this.currentFilter])
+      .subscribe(filters => {
+        this.filters = this.filters.concat(filters);
+      }
+      );
+  }
+
+  selectFilterByName(defaultFilterName: string) {
+    this.selectFilter(this.filters.find(f => f.name === defaultFilterName));
+  }
+
+  removeSearchField(searchField: { group: string, attribute: string }) {
+    let index = this.searchFields.findIndex(sf => sf.group === searchField.group && sf.attribute === searchField.attribute);
+    this.searchFields.splice(index, 1);
+  }
+
+  selectResultType(type: any) {
+    this.currentFilter.resultType = type.type;
+    this.updateSearchAttributesForCurrentResultType();
+  }
+
+  updateSearchAttributesForCurrentResultType() {
+    if (this.allSearchAttributes.hasOwnProperty(this.getSelectedDefinition())) {
+      this.allSearchAttributesForCurrentResultType = this.allSearchAttributes[this.getSelectedDefinition()];
+    }
+  }
+
+  getSearchDefinition(type: string) {
+    return this.definitions.find(def => def.type === type);
+  }
+
+  getSelectedDefinition() {
+    let def = this.getSearchDefinition(this.currentFilter.resultType);
+    if (def) {
+      return def.value;
+    }
+  }
+
+  onSearch() {
+    let query;
+    if (this.isBoxChecked) {
+      query = this.searchService.convertToQuery(this.currentFilter, this.allSearchAttributes, this.viewComponent.selectedView);
+    } else {
+      let filter = classToClass(this.currentFilter);
+      filter.conditions = [];
+      query = this.searchService.convertToQuery(filter, this.allSearchAttributes, this.viewComponent.selectedView);
+    }
+    this.queryService.query(query)
+      .do(result => this.generateWarningsIfMaxResultsReached(result))
+      .subscribe(result => {
+        this.results = result;
+        this.isSearchResultsOpen = true;
+        this.searchExecuted = true;
+      },
+      error => this.notificationService.notifyError('Suchanfrage konnte nicht bearbeitet werden!', error)
+      );
+  }
+
+  generateWarningsIfMaxResultsReached(result: SearchResult) {
+    let resultsPerSource = result.rows
+      .map(r => r.source)
+      .reduce((prev, item) => { (prev[item]) ? prev[item] += 1 : prev[item] = 1; return prev; }, {});
+
+    Object.keys(resultsPerSource)
+      .filter(source => resultsPerSource[source] > this.maxResults)
+      .forEach(source => this.notificationService.notifyWarn(
+        'Zu viele Suchergebnisse',
+        `Es werden die ersten ${this.maxResults} Suchergebnisse aus ${source}
+          angezeigt. Bitte schränken Sie die Suchanfrage weiter ein.`));
+  }
+
+  calcCurrentSearch() {
+    let environments = this.currentFilter.environments;
+    let conditions = this.currentFilter.conditions;
+    let type = this.getSearchDefinition(this.currentFilter.resultType).value;
+    this.layout = SearchLayout.createSearchLayout(environments, this.allSearchAttributesForCurrentResultType, conditions);
+  }
+
+  onFilterChanged(filter: SearchFilter) {
+    this.currentFilter = classToClass(filter);
+    this.dropdownModel.forEach(item => item.selected = (this.currentFilter.environments.findIndex(i => i === item.id) >= 0));
+    this.selectedEnvironmentsChanged(this.dropdownModel);
+    this.calcCurrentSearch();
+  }
+
+  selectFilter(filter: SearchFilter) {
+    this.filterService.setSelectedFilter(filter);
+  }
+
+  resetConditions(e: Event) {
+    e.stopPropagation();
+    this.currentFilter.conditions.forEach(cond => cond.value = []);
+    this.onFilterChanged(this.currentFilter);
+  }
+
+  clearResultlist(e: Event) {
+    e.stopPropagation();
+    this.results = new SearchResult();
+  }
+
+  deleteFilter(e: Event) {
+    e.stopPropagation();
+    if (this.currentFilter.name === this.filterService.NO_FILTER_NAME
+          || this.currentFilter.name === this.filterService.NEW_FILTER_NAME) {
+      this.notificationService
+        .notifyInfo('Kein Filter ausgewählt.',
+        'Der Vorgang konnte nicht durchgeführt werden, da kein gespeicherter Filter zum Löschen ausgewählt wurde.');
+    } else {
+      this.layout = new SearchLayout;
+      this.filterService.deleteFilter(this.currentFilter.name).subscribe(
+        () => {
+          this.loadFilters();
+          this.selectFilter(new SearchFilter(this.filterService.NO_FILTER_NAME, [], 'Test', '', []));
+        },
+        () => this.notificationService
+          .notifyError('Server Error.', 'Der Vorgang konnte nicht durchgeführt werden, da ein Serverfehler aufgetreten ist.')
+      );
+    }
+  }
+
+  saveFilter(e: Event) {
+    e.stopPropagation();
+    if (this.filters.find(f => f.name === this.filterName) !== undefined) {
+      this.childSaveModal.hide();
+      this.overwriteDialogComponent.showOverwriteModal('ein Filter').subscribe(ovw => this.saveFilter2(ovw));
+    } else {
+      this.saveFilter2(true);
+    }
+  }
+
+  saveFilter2(save: boolean) {
+    if (save) {
+      let filter = this.currentFilter;
+      filter.name = this.filterName;
+      this.filterService.saveFilter(filter).subscribe(
+        () => {
+          this.loadFilters();
+          this.selectFilter(filter);
+        }
+      );
+      this.childSaveModal.hide();
+    } else {
+      this.childSaveModal.show();
+    }
+  }
+
+  removeCondition(condition: Condition) {
+    this.currentFilter.conditions = this.currentFilter.conditions
+      .filter(c => !(c.type === condition.type && c.attribute === condition.attribute));
+
+    this.calcCurrentSearch();
+  }
+
+  selected2Basket(e: Event) {
+    e.stopPropagation();
+    this.tableViewComponent.selectedRows.forEach(row => this.basketService.add(row.getItem()));
+  }
+
+  showSaveModal(e: Event) {
+    e.stopPropagation();
+    if (this.currentFilter.name === this.filterService.NO_FILTER_NAME
+          	|| this.currentFilter.name === this.filterService.NEW_FILTER_NAME) {
+      this.filterName = '';
+    } else {
+      this.filterName = this.currentFilter.name;
+    }
+    this.childSaveModal.show();
+  }
+
+  showSearchFieldsEditor(e: Event, conditions?: Condition[]) {
+    e.stopPropagation();
+    this.editSearchFieldsComponent.show(conditions).subscribe(conds => {
+      if (!conditions) {
+        let filter = new SearchFilter(this.filterService.NEW_FILTER_NAME, this.currentFilter.environments, 'Test', '', conds);
+        this.selectFilter(filter);
+      }
+      this.currentFilter.conditions = conds;
+      this.calcCurrentSearch();
+    });
+  }
+
+  addSelectionToBasket() {
+    this.basketService.addAll(this.tableViewComponent.selectedRows.map(row => row.getItem()));
+  }
+
+  mapSourceNameToName(sourceName: string) {
+    return NodeService.mapSourceNameToName(this.environments, sourceName);
+  }
+
+  getSaveFilterBtnTitle () {
+    return this.filterName ? this.TtlSaveFilter : this.TtlNoNameSet;
+  }
+
+  getAdvancedSearchCbxTitle() {
+    return this.isBoxChecked ? this.TtlDisableAdvancedSearch : this.TtlEnableAdvancedSearch;
+  }
+
+  private loadSearchAttributes(environments: string[]) {
+    this.searchService.loadSearchAttributesStructured(environments)
+      .subscribe(
+        attrs => { this.allSearchAttributes = attrs; this.updateSearchAttributesForCurrentResultType(); },
+        error => this.notificationService.notifyError('Attribute konnten nicht geladen werden!', error));
+  }
+
+  onRowSelect(e: any) {
+    if (this.lazySelectedRow !== e.data) {
+      this.selectedRow = e.data;
+      this.filterName = e.data.name;
+    } else {
+      this.selectedRow = undefined;
+      this.filterName = '';
+    }
+    this.lazySelectedRow = this.selectedRow;
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.module.ts
new file mode 100644
index 0000000..c6fbf94
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.module.ts
@@ -0,0 +1,54 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+import { DatePipe } from '@angular/common';

+

+import { MDMCoreModule } from '../core/mdm-core.module';

+

+import { MDMSearchComponent } from './mdm-search.component';

+import { SearchConditionComponent } from './search-condition.component';
+import { SearchDatepickerComponent } from './search-datepicker.component';
+import { EditSearchFieldsComponent } from './edit-searchFields.component';

+

+import { TableViewModule } from '../tableview/tableview.module';

+import {SearchService} from './search.service';

+import {FilterService} from './filter.service';

+

+import {SearchattributeTreeModule} from '../searchattribute-tree/searchattribute-tree.module';

+import {AutoCompleteModule} from 'primeng/primeng';

+

+@NgModule({

+  imports: [

+    MDMCoreModule,

+    TableViewModule,

+    SearchattributeTreeModule,

+    AutoCompleteModule

+  ],

+  declarations: [

+    MDMSearchComponent,

+    SearchConditionComponent,

+    SearchDatepickerComponent,

+    EditSearchFieldsComponent,

+  ],

+  exports: [

+    MDMSearchComponent,

+    EditSearchFieldsComponent

+  ],

+  providers: [

+    SearchService,

+    FilterService,

+    DatePipe

+  ],

+})

+export class MDMSearchModule {

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-base.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-base.ts
new file mode 100644
index 0000000..7e6a4d5
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-base.ts
@@ -0,0 +1,46 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+export class SearchBase<T>{
+  value: T;
+  t: string;
+  a: string;
+  key: string;
+  label: string;
+  required: boolean;
+  order: number;
+  controlType: string;
+  active: boolean;
+  constructor(options: {
+      value?: T,
+      t?: string,
+      a?: string,
+      key?: string,
+      label?: string,
+      required?: boolean,
+      order?: number,
+      controlType?: string,
+      active?: boolean
+    } = {}) {
+    this.value = options.value;
+    this.t = options.t;
+    this.a = options.a;
+    this.key = options.key || '';
+    this.label = options.label || '';
+    this.required = !!options.required;
+    this.order = options.order === undefined ? 1 : options.order;
+    this.controlType = options.controlType || '';
+    this.active = options.active || false;
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.css b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.css
new file mode 100644
index 0000000..3d2177e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.css
@@ -0,0 +1,48 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+table.searchdefinition td { vertical-align: middle; padding: 4px 8px; }

+

+p-autoComplete >>> .ui-inputtext.ui-state-focus {

+  border: 1px solid #66afe9 !important;

+  outline: 0 !important;

+  box-shadow: 0 0 8px rgba(102,175,233,.6) !important;

+}

+

+p-autoComplete >>> .ui-autocomplete.ui-autocomplete-multiple .ui-autocomplete-multiple-container {

+  padding: 2.5px 8px !important;

+  border: 1px solid #ccc;

+  color: #555;

+  box-shadow: inset 0 1px 1px rgba(0,0,0,.075);

+  width: 100%;

+}

+

+p-autoComplete >>> .ui-autocomplete-multiple-container.ui-inputtext {

+  overflow: inherit !important;

+}

+

+p-autoComplete >>> .ui-autocomplete {

+  width: 100%;

+}

+

+p-autoComplete >>> .ui-widget:disabled{

+  cursor: not-allowed !important;

+  background-color: #eee;

+  color: #555 !important;

+  opacity: 1;

+}

+p-autoComplete >>> .ui-state-disabled{

+  cursor: not-allowed !important;

+  background-color: #eee !important;

+  border-color: #e3e3e3 !important;

+  opacity: 1;

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.html
new file mode 100644
index 0000000..2641247
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.html
@@ -0,0 +1,43 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<td style="vertical-align: middle">{{condition.type}}</td>

+<td style="vertical-align: middle">{{condition.attribute}}</td>

+<td style="vertical-align: middle">

+  <div class="btn-group" dropdown style="width: 66px;">

+    <button id="operator" type="button" title="{{TtlSelectSearchOperator}}" class="btn btn-default btn-sm dropdown-toggle" dropdownToggle aria-haspopup="true" aria-expanded="false" [disabled]="disabled">

+      {{getOperatorName(condition.operator)}} <span class="caret"></span>

+    </button>

+    <ul class="dropdown-menu" dropdownMenu>

+      <li *ngFor="let op of getOperators()">

+        <a class="dropdown-item" (click)="setOperator(op)">{{getOperatorName(op)}}</a>

+      </li>

+    </ul>

+  </div>

+</td>

+<td [ngSwitch]="condition.valueType">

+  <p-autoComplete *ngSwitchCase="'string'"

+    [(ngModel)]="condition.value"

+    [suggestions]="displayedSuggestions"

+    (completeMethod)="updateSuggestions($event)"

+    [multiple]="true"

+    [delay]="0"

+    [size]="500"

+    [scrollHeight]="'50vh'"

+    placeholder="Wert"

+    [disabled]="disabled"

+    (keyup.enter)="onEnter($event)">

+  </p-autoComplete>

+  <div search-datepicker *ngSwitchCase="'date'" (onSetValue)="setValue($event)" style="width: 100%" [disabled]="disabled" [initialValue]="condition.value"></div>

+  <input *ngSwitchDefault type="text" class="form-control input-sm" placeholder="Wert" [value]="condition.value" (input)="setValue($event.target.value)" [disabled]="disabled">

+</td>

+<td style="vertical-align: middle"><button id="remove" type="button" class="btn btn-default btn-sm glyphicon glyphicon-remove remove" (click)="remove()" [disabled]="disabled"></button></td>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.ts
new file mode 100644
index 0000000..95cb793
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.ts
@@ -0,0 +1,113 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import {Component, Input, Output, EventEmitter, OnChanges, SimpleChanges, ViewChild} from '@angular/core';

+import {FormGroup} from '@angular/forms';

+import {SearchBase} from './search-base';

+import {LocalizationService} from '../localization/localization.service';

+import {FilterService, SearchFilter, Condition, Operator, OperatorUtil} from './filter.service';

+import {Node} from '../navigator/node';

+

+import {PropertyService} from '../core/property.service';

+import {QueryService} from '../tableview/query.service';

+import {AutoCompleteModule} from 'primeng/primeng';

+import {AutoComplete} from 'primeng/primeng';

+

+@Component({

+  selector: '[search-condition]',

+  templateUrl: 'search-condition.component.html',

+  styleUrls: ['search-condition.component.css'],

+})

+export class SearchConditionComponent implements OnChanges {

+

+  readonly TtlSelectSearchOperator = 'Suchoperator auswählen';

+

+  @Input() env: string;

+  @Input() condition: Condition;

+  @Input() form: FormGroup;

+  @Input() disabled: boolean;

+  @Input() selectedEnvs: Node[];

+  @Output() onRemove = new EventEmitter<Condition>();

+

+  suggestions: string[];

+  displayedSuggestions: string[] = [];

+  lastQuery: string;

+

+  @ViewChild(AutoComplete) primeAutoCompleteComponent: AutoComplete;

+

+  constructor(private localservice: LocalizationService,

+    private prop: PropertyService,

+    private queryService: QueryService) { }

+

+  ngOnChanges(changes: SimpleChanges) {

+    if (changes['selectedEnvs'] && this.condition.valueType === 'string') {

+      this.setAutoCompleteValues();

+    }

+  }

+

+  onEnter(e: Event) {

+    let hl = this.primeAutoCompleteComponent.highlightOption;

+    if (hl === undefined && this.lastQuery !== '' || hl !== undefined && this.displayedSuggestions.find(s => s === hl) === undefined) {

+      if (this.primeAutoCompleteComponent.value[this.primeAutoCompleteComponent.value.length - 1] === hl) {

+        this.primeAutoCompleteComponent.value.pop();

+      }

+      this.primeAutoCompleteComponent.selectItem(this.lastQuery);

+      this.lastQuery = '';

+    }

+    this.primeAutoCompleteComponent.highlightOption = undefined;

+    this.displayedSuggestions = [];

+  }

+

+  setAutoCompleteValues() {

+    this.queryService.suggestValues(this.env === 'Global' ?

+        this.selectedEnvs.map(env => env.sourceName) :

+        [this.env], this.condition.type, this.condition.attribute)

+      .subscribe(values => this.suggestions = Array.from(new Set<string>(values)));

+  }

+

+  updateSuggestions(e: any) {

+    if (this.suggestions) {

+    this.displayedSuggestions =

+      [...this.suggestions.filter(sug => sug.toLowerCase().indexOf(e.query.toLowerCase()) > -1).slice(0, 10)];

+    }

+    this.lastQuery = e.query;

+  }

+

+  getTrans(label: string) {

+    let a = label.split('.');

+    return this.localservice.getTranslation(a[0], a[1]);

+  }

+

+  getOperators() {

+    return OperatorUtil.values();

+  }

+

+  getOperatorName(op: Operator) {

+    return OperatorUtil.toString(op);

+  }

+

+  setOperator(operator: Operator) {

+    this.condition.operator = operator;

+  }

+

+  setValue(value: string) {

+    this.condition.value = [value];

+  }

+

+  setValues(value: string[]) {

+    this.condition.value = value;

+  }

+

+  remove() {

+    this.onRemove.emit(this.condition);

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-control.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-control.service.ts
new file mode 100644
index 0000000..de45890
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-control.service.ts
@@ -0,0 +1,32 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Injectable} from '@angular/core';
+import {FormGroup, FormBuilder, Validators} from '@angular/forms';
+import {SearchBase} from './search-base';
+
+@Injectable()
+export class SearchControlService {
+  constructor(private fb: FormBuilder) { }
+
+  toControlGroup(searches) {
+    let group = {};
+    for (let i = 0; i < searches.length; i++) {
+      searches[i].items.forEach(search => {
+        group[search.key] = search.required ? [search.value || '', Validators.required] : [search.value || ''];
+      });
+    }
+    return this.fb.group(group);
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-datepicker.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-datepicker.component.html
new file mode 100644
index 0000000..7f8f5c9
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-datepicker.component.html
@@ -0,0 +1,24 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<div class="input-group" dropdown [autoClose]="'outsideClick'" style="width: 100%">

+  <input type="text" class="form-control input-sm" placeholder="TT.MM.JJJJ HH:MM" [value]="date | date:'dd.MM.yyyy HH:mm'" (input)="setDate($event.target.value)" [disabled]="disabled">

+  <span *ngIf="!disabled" class="input-group-addon"  title="{{TtlSelectDate}}" style="cursor:pointer; border-radius: 4px; padding-right: 12px; width: 1%; border-top-left-radius: 0; border-bottom-left-radius: 0;" dropdownToggle>

+    <em class="glyphicon glyphicon-calendar"></em>

+  </span>

+  <span *ngIf="disabled" class="input-group-addon" style="cursor: not-allowed; border-radius: 4px; padding-right: 12px; width: 1%; border-top-left-radius: 0; border-bottom-left-radius: 0;">

+    <em class="glyphicon glyphicon-calendar"></em>

+  </span>

+  <ul class="dropdown-menu" dropdownMenu>

+    <datepicker [(ngModel)]="date" [showWeeks]="false" (selectionDone)="onSelectionDone($event);"></datepicker>

+  </ul>

+</div>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-datepicker.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-datepicker.component.ts
new file mode 100644
index 0000000..f98ccc5
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-datepicker.component.ts
@@ -0,0 +1,52 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+import {Component, OnInit, Output, Input, EventEmitter} from '@angular/core';
+import { DatePipe } from '@angular/common';
+
+@Component({
+  selector: '[search-datepicker]',
+  templateUrl: 'search-datepicker.component.html'
+})
+export class SearchDatepickerComponent implements OnInit {
+
+  readonly TtlSelectDate = 'Datum auswählen';
+
+  @Output() onSetValue = new EventEmitter<string>();
+  @Input() disabled = false;
+  @Input() initialValue: string[];
+
+  date: any;
+  constructor(private datePipe: DatePipe) {}
+
+  ngOnInit() {
+    if (this.initialValue.length > 0) {
+      this.date = this.initialValue[0];
+      this.setDate(this.initialValue[0]);
+    }
+  }
+
+  setDate(inputDate: string) {
+    let dateString = inputDate.split(' ')[0];
+    let days = dateString.split('.')[0];
+    let month = dateString.split('.')[1];
+    let year = dateString.split('.')[2];
+    let timeString = inputDate.split(' ')[1];
+    let date = new Date([month, days, year].join('.') + ' ' + timeString);
+    if (date.toString() !== 'Invalid Date') { this.onSelectionDone(date); };
+  }
+
+  onSelectionDone(date: Date) {
+    this.onSetValue.emit(this.datePipe.transform(date, 'yyyy-MM-dd' + 'T' + 'HH:mm:ss'));
+  }
+
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-dropdown.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-dropdown.ts
new file mode 100644
index 0000000..48a7c06
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-dropdown.ts
@@ -0,0 +1,25 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {SearchBase} from './search-base';
+
+export class DropdownSearch extends SearchBase<string> {
+  controlType = 'dropdown';
+  options: {key: string, value: string}[] = [];
+
+  constructor(options: {} = {}) {
+    super(options);
+    this.options = options['options'] || [];
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-textbox.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-textbox.ts
new file mode 100644
index 0000000..6d077f6
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-textbox.ts
@@ -0,0 +1,25 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {SearchBase} from './search-base';
+
+export class TextboxSearch extends SearchBase<string> {
+  controlType = 'textbox';
+  type: string;
+
+  constructor(options: {} = {}) {
+    super(options);
+    this.type = options['type'] || '';
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search.service.spec.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search.service.spec.ts
new file mode 100644
index 0000000..167389f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search.service.spec.ts
@@ -0,0 +1,311 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { DebugElement } from '@angular/core';

+import { By } from '@angular/platform-browser';

+import { Observable } from 'rxjs/Observable';

+import { BaseRequestOptions, Http, HttpModule, Response, ResponseOptions } from '@angular/http';

+import { ComponentFixture, async, TestBed, inject } from '@angular/core/testing';

+import { MockBackend } from '@angular/http/testing';

+

+import {SearchService, SearchAttribute, SearchLayout} from './search.service';

+import {PreferenceService, Preference, Scope} from '../core/preference.service';

+import {PropertyService} from '../core/property.service';

+import {LocalizationService} from '../localization/localization.service';

+import {NodeService} from '../navigator/node.service';

+import {QueryService} from '../tableview/query.service';

+import {MDMNotificationService} from '../core/mdm-notification.service';

+

+import {Condition, Operator} from './filter.service';

+

+class TestPreferenceService {

+  getPreference(key?: string): Observable<Preference[]> {

+    return Observable.of([

+    {

+      id: 1,

+      key: 'ignoredAttributes',

+      scope: Scope.USER,

+      source: null,

+      user: 'testUser',

+      value: '[\"*.Name\", \"TestStep.Id\", \"Measurement.*\"]'

+      // value: '[\"*.MimeType\", \"TestStep.Sortindex\"]'

+    }

+  ]);

+  }

+}

+

+describe ( 'SearchService', () => {

+

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        LocalizationService,

+        PropertyService,

+        {

+          provide: PreferenceService,

+          useClass: TestPreferenceService

+        },

+        SearchService,

+        MDMNotificationService,

+        NodeService,

+        QueryService,

+        MockBackend,

+        BaseRequestOptions,

+        {

+          provide: Http,

+          useFactory: (backend, options) => new Http(backend, options),

+          deps: [MockBackend, BaseRequestOptions]

+        }]

+    });

+  });

+
+  describe('groupByEnv()', () => {

+    it('should group attributes by environment', () => {

+      let attributes = [

+        new SearchAttribute('env1', 'Test', 'Name'),

+        new SearchAttribute('env1', 'Vehicle', 'Name'),

+        new SearchAttribute('env2', 'Test', 'Name'),

+        new SearchAttribute('env2', 'Uut', 'Name'),

+      ];
+
+      let conditionsPerEnv = SearchLayout.groupByEnv(attributes);

+

+      expect(Object.getOwnPropertyNames(conditionsPerEnv))

+        .toContain('env1', 'env2');

+

+      expect(conditionsPerEnv['env1'])

+        .toContain(attributes[0], attributes[1]);

+

+      expect(conditionsPerEnv['env2'])

+        .toContain(attributes[2], attributes[3]);

+    });

+  });

+

+  describe('createSearchLayout()', () => {

+    it('should create a searchLayout object from conditions', () => {

+      let cond1 = new Condition('Test', 'Name', Operator.EQUALS, []);

+      let cond2 = new Condition('Vehicle', 'Name', Operator.EQUALS, []);

+

+      let attributes = {

+        'env1': [

+          new SearchAttribute('env1', 'Test', 'Name'),

+          new SearchAttribute('env1', 'Vehicle', 'Name'),

+        ],

+        'env2': [

+          new SearchAttribute('env2', 'Test', 'Name'),

+          new SearchAttribute('env2', 'Uut', 'Name'),

+        ]

+      };

+

+      let searchLayout = SearchLayout.createSearchLayout(['env1', 'env2'], attributes, [cond1, cond2]);

+

+      expect(searchLayout.getSourceNames())

+        .toEqual(['Global', 'env1']);

+

+      expect(searchLayout.getConditions('Global'))

+        .toEqual([cond1]);

+

+      expect(searchLayout.getConditions('env1'))

+        .toEqual([cond2]);

+

+    });

+  });

+
+  describe('convert()', () => {

+    it('should convert conditions to filter string',  async(inject([SearchService, MockBackend], (service, mockBackend) => {

+      let cond1 = new Condition('Test', 'Name', Operator.LIKE, ['PBN*']);

+      let cond2 = new Condition('Vehicle', 'Name', Operator.EQUALS, ['car']);

+

+      let attributes = [

+          new SearchAttribute('env1', 'Test', 'Name'),

+          new SearchAttribute('env1', 'Vehicle', 'Name'),

+          new SearchAttribute('env2', 'Test', 'Name'),

+          new SearchAttribute('env2', 'Uut', 'Name'),

+        ];

+

+      let filter = service.convertEnv('env1', [cond1, cond2], attributes, 'test');

+

+      expect(filter.sourceName).toEqual('env1');

+      expect(filter.filter).toEqual('Test.Name lk PBN* and Vehicle.Name eq car');

+      expect(filter.searchString).toEqual('test');

+

+    })));

+  });

+

+  describe('loadSearchAttributes()', () => {

+    it('should return filtered search attributes for env',

+      async(inject([SearchService, MockBackend],

+        (searchService, mockBackend) => {

+      mockBackend.connections.subscribe(conn => {

+        let mockResponse = {

+          data: [

+            {

+              boType: 'Test',

+              attrName: 'Name',

+              valueType: 'STRING',

+              criteria: '*'

+            },

+            {

+              boType: 'Test',

+              attrName: 'Id',

+              valueType: 'LONG',

+              criteria: '*'

+            },

+            {

+              boType: 'TestStep',

+              attrName: 'Name',

+              valueType: 'STRING',

+              criteria: '*'

+            },

+            {

+              boType: 'TestStep',

+              attrName: 'MimeType',

+              valueType: 'STRING',

+              criteria: '*'

+            },

+            {

+              boType: 'TestStep',

+              attrName: 'Id',

+              valueType: 'LONG',

+              criteria: '*'

+            },

+            {

+              boType: 'Measurement',

+              attrName: 'Name',

+              valueType: 'STRING',

+              criteria: '*'

+            },

+            {

+              boType: 'Measurement',

+              attrName: 'Id',

+              valueType: 'LONG',

+              criteria: '*'

+            }

+        ]};

+        if (conn.request.url === searchService._prop.getUrl() + '/mdm/environments/TestEnv//searchattributes') {

+          conn.mockRespond(new Response(new ResponseOptions({ body: mockResponse })));

+        } else if (conn.request.url === searchService._prop.getUrl() + '/mdm/environments/' + 'TestEnv2' + '/' + '/searchattributes') {

+          conn.mockRespond(new Response(new ResponseOptions({ body: mockResponse })));

+        }

+      });

+      let ans = [

+        new SearchAttribute('TestEnv', 'Test', 'Id', 'LONG', '*'),

+        new SearchAttribute('TestEnv', 'TestStep', 'MimeType', 'STRING', '*'),

+      ];

+      searchService.loadSearchAttributes('', 'TestEnv').subscribe(sas => {

+        expect(sas).toEqual(ans);

+      });

+    })));

+  });

+

+  describe('getFilters()', () => {

+    it('should retrun ignoredAttributes in a string array',

+      async(inject([SearchService],

+        (searchService) => {

+      expect(searchService.getFilters(undefined)).toEqual(['*.Name', 'TestStep.Id', 'Measurement.*']);

+    })));

+  });

+

+  describe('filterIgnoredAttributes', () => {

+    it('should return searchAttributes without the ignored ones',

+      async(inject([SearchService], (searchService) => {

+

+    let input = [

+      new SearchAttribute('TestEnv', 'Test', 'Name', 'STRING'),

+      new SearchAttribute('TestEnv', 'Test', 'MimeType', 'STRING'),

+      new SearchAttribute('TestEnv', 'Test', 'Id', 'LONG'),

+      new SearchAttribute('TestEnv', 'TestStep', 'Name', 'STRING'),

+      new SearchAttribute('TestEnv', 'TestStep', 'MimeType', 'STRING'),

+      new SearchAttribute('TestEnv', 'TestStep', 'Id', 'LONG'),

+      new SearchAttribute('TestEnv', 'Measurement', 'Name', 'STRING'),

+      new SearchAttribute('TestEnv', 'Measurement', 'MimeType', 'STRING'),

+      new SearchAttribute('TestEnv', 'Measurement', 'Id', 'LONG')

+    ];

+

+  let ans = [

+    new SearchAttribute('TestEnv', 'Test', 'MimeType', 'STRING'),

+    new SearchAttribute('TestEnv', 'Test', 'Id', 'LONG'),

+    new SearchAttribute('TestEnv', 'TestStep', 'MimeType', 'STRING')

+  ];

+

+  expect(searchService.filterIgnoredAttributes(undefined, input)).toEqual(ans);

+    })));

+  });

+

+  describe('getSearchAttributes()', () => {

+    it('should return filtered search attributes for env',

+      async(inject([SearchService, MockBackend],

+        (searchService, mockBackend) => {

+      mockBackend.connections.subscribe(conn => {

+        let mockResponse = {

+          data: [

+            {

+              boType: 'Test',

+              attrName: 'Name',

+              valueType: 'STRING',

+              criteria: '*'

+            },

+            {

+              boType: 'Test',

+              attrName: 'Id',

+              valueType: 'LONG',

+              criteria: '*'

+            },

+            {

+              boType: 'TestStep',

+              attrName: 'Name',

+              valueType: 'STRING',

+              criteria: '*'

+            },

+            {

+              boType: 'TestStep',

+              attrName: 'MimeType',

+              valueType: 'STRING',

+              criteria: '*'

+            },

+            {

+              boType: 'TestStep',

+              attrName: 'Id',

+              valueType: 'LONG',

+              criteria: '*'

+            },

+            {

+              boType: 'Measurement',

+              attrName: 'Name',

+              valueType: 'STRING',

+              criteria: '*'

+            },

+            {

+              boType: 'Measurement',

+              attrName: 'Id',

+              valueType: 'LONG',

+              criteria: '*'

+            }

+        ]};

+        if (conn.request.url === searchService._prop.getUrl() + '/mdm/environments/TestEnv//searchattributes') {

+          conn.mockRespond(new Response(new ResponseOptions({ body: mockResponse })));

+        } else if (conn.request.url === searchService._prop.getUrl() + '/mdm/environments/TestEnv2//searchattributes') {

+          conn.mockRespond(new Response(new ResponseOptions({ body: mockResponse })));

+        }

+      });

+      let ans = [

+        new SearchAttribute('TestEnv', 'Test', 'Id', 'LONG', '*'),

+        new SearchAttribute('TestEnv', 'TestStep', 'MimeType', 'STRING', '*'),

+        new SearchAttribute('TestEnv2', 'Test', 'Id', 'LONG', '*'),

+        new SearchAttribute('TestEnv2', 'TestStep', 'MimeType', 'STRING', '*'),

+      ];

+

+      searchService.getSearchAttributesPerEnvs(['TestEnv', 'TestEnv2'], '').subscribe(sas => expect(sas).toEqual(ans));

+    })));

+  });

+});

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/search/search.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search.service.ts
new file mode 100644
index 0000000..8301283
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search.service.ts
@@ -0,0 +1,282 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import {Injectable} from '@angular/core';
+import {Http, Response} from '@angular/http';
+import {Observable} from 'rxjs/Observable';
+
+import {MDMNotificationService} from '../core/mdm-notification.service';
+import {PropertyService} from '../core/property.service';
+import {LocalizationService} from '../localization/localization.service';
+import { Preference, PreferenceService, Scope } from '../core/preference.service';
+import {deserializeArray, plainToClass} from 'class-transformer';
+
+import {SearchFilter, Condition, Operator, OperatorUtil} from './filter.service';
+import {Query, Filter} from '../tableview/query.service';
+import {View} from '../tableview/tableview.service';
+
+export class SearchLayout {
+  map: { [sourceNames: string]: Condition[] } = {};
+
+  public static createSearchLayout(envs: string[], attributesPerEnv: { [env: string]: SearchAttribute[] }, conditions: Condition[]) {
+    let conditionsWithSortIndex = conditions.map((c, i) => { c.sortIndex = i; return c; });
+
+    let result = new SearchLayout();
+    let attribute2Envs = SearchLayout.mapAttribute2Environments(envs, attributesPerEnv);
+    let globalEnv = 'Global';
+    Object.keys(attribute2Envs).forEach(attr => {
+      let c = conditionsWithSortIndex.find(cond => cond.type + '.' + cond.attribute === attr);
+      if (c) {
+        if (attribute2Envs[attr].length === envs.length) {
+          result.add(globalEnv, c);
+        } else {
+          attribute2Envs[attr].forEach(e => result.add(e, c));
+        }
+      }
+    });
+    return result;
+  }
+
+  public static groupByEnv(attrs: SearchAttribute[]) {
+    let attributesPerEnv: { [environment: string]: SearchAttribute[] } = {};
+
+    attrs.forEach(attr => {
+      attributesPerEnv[attr.source] = attributesPerEnv[attr.source] || [];
+      attributesPerEnv[attr.source].push(attr);
+    });
+    return attributesPerEnv;
+  }
+
+  private static mapAttribute2Environments(envs: string[], attributesPerEnv: { [environment: string]: SearchAttribute[] }) {
+    let attribute2Envs: { [attribute: string]: string[] } = {};
+
+    Object.keys(attributesPerEnv)
+      .filter(env => envs.find(e => e === env))
+      .forEach(env =>
+        attributesPerEnv[env].forEach(sa => {
+          let attr = sa.boType + '.' + sa.attrName;
+
+          attribute2Envs[attr] = attribute2Envs[attr] || [];
+          attribute2Envs[attr].push(env);
+        })
+      );
+
+    return attribute2Envs;
+  }
+
+  getSourceNames() {
+    return Object.keys(this.map).sort((s1, s2) => {
+      if (s1 === 'Global') {
+        return -1;
+      } else if ( s2 === 'Global') {
+        return 1;
+      } else if (s1) {
+        return s1.localeCompare(s2);
+      } else {
+        return -1;
+      }
+    });
+  }
+
+  getConditions(sourceName: string) {
+    return this.map[sourceName].sort((c1, c2) => c1.sortIndex - c2.sortIndex) || [];
+  }
+
+  getSourceName(condition: Condition) {
+    if (condition) {
+      let sourceName;
+
+      Object.keys(this.map)
+        .forEach(env => {
+            if(this.map[env].find(c => c.type === condition.type && c.attribute === condition.attribute))
+            {
+              sourceName = env;
+            }
+          }
+        );
+
+      return sourceName;
+    }
+  }
+
+  set(sourceName: string, conditions: Condition[]) {
+    this.map[sourceName] = conditions;
+  }
+
+  add(sourceName: string, condition: Condition) {
+    this.map[sourceName] = this.map[sourceName] || [];
+    this.map[sourceName].push(condition);
+  }
+}
+
+export class SearchAttribute {
+  source: string;
+  boType: string;
+  attrName: string;
+  valueType: string;
+  criteria: string;
+
+  constructor(source: string, boType: string, attrName: string, valueType?: string, criteria?: string) {
+    this.source = source;
+    this.boType = boType;
+    this.attrName = attrName;
+    this.valueType = valueType || 'string';
+    this.criteria = criteria || '';
+  }
+}
+
+export class SearchDefinition {
+  key: string;
+  value: string;
+  type: string;
+  label: string;
+}
+
+@Injectable()
+export class SearchService {
+
+  private _searchUrl: string;
+  private errorMessage: string;
+
+  private defs: SearchAttribute[];
+
+  ignoreAttributesPrefs: Preference[] = [];
+
+  private cachedAttributes: Observable<any>;
+
+  constructor(private http: Http,
+    private localService: LocalizationService,
+    private _prop: PropertyService,
+    private preferenceService: PreferenceService,
+    private notificationService: MDMNotificationService) {
+          this.preferenceService.getPreference('ignoredAttributes')
+              .subscribe( prefs => this.ignoreAttributesPrefs = this.ignoreAttributesPrefs.concat(prefs));
+    }
+
+  loadSearchAttributes(type: string, env: string) {
+    return this.http.get(this._prop.getUrl('/mdm/environments/' + env + '/' + type + '/searchattributes'))
+      .map(response => <SearchAttribute[]>response.json().data)
+      .map(sas => sas.map(sa => { sa.source = env; return sa; }))
+      .map(sas => this.filterIgnoredAttributes(env, sas));
+  }
+
+  getDefinitionsSimple() {
+    return Observable.of([
+      <SearchDefinition>{ key: '1', value: 'tests', type: 'Test', label: 'Versuche' },
+      <SearchDefinition>{ key: '2', value: 'teststeps', type: 'TestStep', label: 'Versuchsschritte' },
+      <SearchDefinition>{ key: '3', value: 'measurements', type: 'Measurement', label: 'Messungen' }
+    ]);
+  }
+
+  getSearchAttributesPerEnvs(envs: string[], type: string) {
+    return Observable.forkJoin(envs.map(env => this.loadSearchAttributes(type, env)
+      .map(sas => sas.map(sa => { sa.source = env; return sa; }))))
+      .map(x => x.reduce(function(explored, toExplore) {
+        return explored.concat(toExplore);
+      }, []));
+  }
+
+  loadSearchAttributesStructured(environments: string[]) {
+    if (!this.cachedAttributes) {
+      this.cachedAttributes = this.getDefinitionsSimple()
+        .map(defs => defs.map(d => d.value))
+        .flatMap(defs => this.loadSearchAttributesForAllDefs(defs, environments))
+        .publishReplay(1)
+        .refCount();
+    }
+    return this.cachedAttributes;
+  }
+
+  loadSearchAttributesForAllDefs(types: string[], environments: string[]) {
+    return Observable.forkJoin(types.map(type => this.loadSearchAttributesForDef(type, environments)))
+      .map(type2AttributesPerEnv =>
+        type2AttributesPerEnv.reduce(
+          function(acc, value) {
+            acc[value.type] = value.attributesPerEnv;
+            return acc; },
+          <{ [type: string]: { [env: string]: SearchAttribute[] }}> {})
+        );
+  }
+
+  loadSearchAttributesForDef(type: string, environments: string[]) {
+    return Observable.forkJoin(environments.map(env => this.loadSearchAttributes(type, env)
+      .catch(error => { console.log("Could not load search attributes for type " + type + " in environment " + env); return Observable.of(<SearchAttribute[]> []); })
+      .map(attrs => { return { 'env': env, 'attributes': attrs}; })))
+      .map(attrsPerEnv => attrsPerEnv.reduce(
+        function(acc, value) {acc[value.env] = value.attributes; return acc; },
+         <{ [env: string]: SearchAttribute[] }> {})
+       )
+      .map(attrsPerEnv => { return { 'type': type, 'attributesPerEnv': attrsPerEnv}; });
+  }
+
+  convertToQuery(searchFilter: SearchFilter, attr: { [type: string]: { [env: string]: SearchAttribute[] }}, view: View) {
+    let q = new Query();
+
+    q.resultType = searchFilter.resultType;
+    if (attr['tests']) {
+      q.filters = this.convert(searchFilter.environments, searchFilter.conditions, attr['tests'], searchFilter.fulltextQuery); // TODO
+    }
+    q.columns = view.columns.map(c => c.type + '.' + c.name);
+    console.log('Query', q);
+
+    return q;
+  }
+
+  convert(envs: string[], conditions: Condition[], attr: { [env: string]: SearchAttribute[] }, fullTextQuery: string): Filter[] {
+    return envs.map(e => this.convertEnv(e, conditions, attr[e], fullTextQuery));
+  }
+
+  convertEnv(env: string, conditions: Condition[], attrs: SearchAttribute[], fullTextQuery: string): Filter {
+
+    let filterString = conditions
+      .map(c => c.value.map(value => c.type + '.' + c.attribute + ' ' + OperatorUtil.toFilterString(c.operator) + ' ' + value).join(' or '))
+      .filter(c => c.length > 0)
+      .join(' and ');
+
+    return new Filter(env, filterString, fullTextQuery);
+  }
+
+  isAttributeIgnored(attributeName: string, sa: SearchAttribute) {
+    let x = attributeName.split('.', 2);
+    let fType = x[0];
+    let fName = x[1];
+    return ((fType === sa.boType || fType === '*') && (fName === sa.attrName || fName === '*'));
+  }
+
+  private filterIgnoredAttributes(environment: string, searchAttributes: SearchAttribute[]) {
+    let filters = this.getFilters(environment);
+    filters.forEach(f =>
+      searchAttributes = searchAttributes.filter(sa => !this.isAttributeIgnored(f, sa))
+    );
+    return searchAttributes;
+  }
+
+  getFilters(source: string): string[] {
+    return this.ignoreAttributesPrefs
+      .filter(p => p.scope !== Scope.SOURCE || p.source === source)
+      .sort(Preference.sortByScope)
+      .map(p => this.parsePreference(p))
+      .reduce((acc, value) => acc.concat(value), []);
+  }
+
+  private parsePreference(pref: Preference) {
+    try {
+        return <string[]> JSON.parse(pref.value);
+    } catch (e) {
+        this.notificationService.notifyError('Einstellung für zu ignorierende Attribute ist fehlerhaft.', e);
+        return [];
+    }
+  }
+
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.component.html
new file mode 100644
index 0000000..a13e1d7
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.component.html
@@ -0,0 +1,22 @@
+<!-- ***************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+**************************************************************************** -->
+
+<p-tree class="mdmtree"
+        [value]="nodes"
+        selectionMode="single"
+        (onNodeExpand)="loadNodes($event)"
+        (onNodeSelect)="nodeSelect($event)">
+
+    <template let-node pTemplate="default">
+        <span [title]="node.label" >{{ node.label }}</span>
+    </template>
+</p-tree>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.component.ts
new file mode 100644
index 0000000..420f824
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.component.ts
@@ -0,0 +1,123 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+import {Component, OnInit, Input, OnChanges, SimpleChanges, EventEmitter} from '@angular/core';
+
+import {SearchService, SearchDefinition, SearchAttribute, SearchLayout} from '../search/search.service';
+import {Preference, PreferenceService} from '../core/preference.service';
+import {Node} from '../navigator/node';
+import {MDMItem} from '../core/mdm-item';
+
+import {IDropdownItem, IMultiselectConfig} from 'ng2-dropdown-multiselect';
+import {TypeaheadMatch} from 'ng2-bootstrap/typeahead';
+import {ModalDirective} from 'ng2-bootstrap';
+import {TreeModule, TreeNode} from 'primeng/primeng';
+
+@Component({
+  selector: 'searchattribute-tree',
+  templateUrl: './searchattribute-tree.component.html'
+})
+export class SearchattributeTreeComponent implements OnChanges {
+
+  @Input() environments: Node[];
+  @Input() searchAttributes: { [env: string]: SearchAttribute[] } = {};
+
+  lastClickTime = 0;
+  nodes: TreeNode[] = [];
+  selectedAttribute: { label: string, group: string, attribute: SearchAttribute };
+  public onNodeSelect$ = new EventEmitter<TreeNode>();
+
+  constructor(private searchService: SearchService) {}
+
+  ngOnChanges(changes: SimpleChanges) {
+    if ((changes['searchAttributes'] || changes['environments']) && this.environments && this.environments.length > 0) {
+      this.nodes = this.environments.map(n => this.mapRootNode(n));
+    }
+  }
+
+  mapRootNode(node: Node) {
+    let item = new MDMItem(node.sourceName, node.type, +node.id);
+
+    return <TreeNode>{
+      label: node.name,
+      leaf: false,
+      type: 'env',
+      data: item
+    };
+  }
+
+  loadNodes(event: any) {
+    if (event.node) {
+      event.node.children = this.getChildren(event.node);
+    }
+  }
+
+  mapType(group: { boType: string, attributes: SearchAttribute[] }) {
+    return <TreeNode>{
+      label: group.boType,
+      leaf: false,
+      type: 'group',
+      data: group.attributes
+    };
+  }
+
+  mapAttribute(attribute: SearchAttribute) {
+    return <TreeNode>{
+      label: attribute.attrName,
+      leaf: true,
+      type: 'attribute',
+      data: attribute
+    };
+  }
+
+  getChildren(node: TreeNode): TreeNode[] {
+    if (node.type === 'env') {
+      return this.getSearchableGroups(node.data.source)
+        .sort((a, b) => a.boType.localeCompare(b.boType))
+        .map(g => this.mapType(g));
+    } else if (node.type === 'group') {
+      return (<SearchAttribute[]> node.data)
+        .sort((a, b) => a.attrName.localeCompare(b.attrName))
+        .map(a => this.mapAttribute(a));
+    } else {
+      return [];
+    }
+  }
+
+  getSearchableGroups(env: string): { boType: string, attributes: SearchAttribute[] }[] {
+    let distinctGroupArray: { boType: string, attributes: SearchAttribute[] }[] = [];
+    if (this.searchAttributes.hasOwnProperty(env)) {
+      this.searchAttributes[env].forEach(attribute => {
+          let item = distinctGroupArray.find(p => p.boType === attribute.boType);
+          if (item && item.attributes.every(a => a.attrName !== attribute.attrName)) {
+            item.attributes.push(attribute);
+          } else if (!item) {
+            distinctGroupArray.push({ boType: attribute.boType, attributes: [attribute]});
+          }
+      });
+    }
+
+    return distinctGroupArray;
+  }
+
+  nodeSelect(event) {
+    this.onNodeSelect$.emit(event.node);
+    if (event.originalEvent.timeStamp - this.lastClickTime < 300) {
+      if (!event.node.expanded && !event.node.children) {
+        this.loadNodes(event);
+      }
+      event.node.expanded = !event.node.expanded;
+    }
+    this.lastClickTime = event.originalEvent.timeStamp;
+  }
+
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.module.ts
new file mode 100644
index 0000000..de3b654
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.module.ts
@@ -0,0 +1,33 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+

+import { MDMCoreModule } from '../core/mdm-core.module';

+import { MDMNavigatorModule } from '../navigator/mdm-navigator.module';

+

+import { SearchattributeTreeComponent } from './searchattribute-tree.component';

+

+@NgModule({

+  imports: [

+    MDMCoreModule,

+    MDMNavigatorModule,

+  ],

+  declarations: [

+    SearchattributeTreeComponent,

+  ],

+  exports: [

+    SearchattributeTreeComponent,

+  ]

+})

+export class SearchattributeTreeModule {

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/editview.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/editview.component.html
new file mode 100644
index 0000000..9a39286
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/editview.component.html
@@ -0,0 +1,101 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<style>

+  .modal-body >>> .ui-tree .ui-tree-container {

+    margin: 0 0 4px 0 !important;

+    width: 100%;

+    overflow: visible;

+  }

+

+  .modal-body >>> .ui-tree {

+    min-height: 50vh !important;

+    max-height: 72vh !important;

+    overflow: auto;

+  }

+

+  .modal-body >>> .ui-tree .ui-treenode .ui-treenode-content .ui-treenode-label {

+    padding-right: .75em;

+  }

+

+  .modal-body {

+    font-size: 14px;

+  }

+

+  >>> .ui-growl {z-index: 999999 !important;}

+</style>
+

+<div bsModal #lgModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="SelectSearchComponents" aria-hidden="true">

+  <div class="modal-dialog modal-lg">

+    <div class="modal-content">

+      <div class="modal-header">

+        <button type="button" class="close" (click)="closeDialog()" aria-label="Close">

+          <span aria-hidden="true">&times;</span>

+        </button>

+        <h4 class="modal-title">{{LblViewEditor}}</h4>

+      </div>

+      <div class="modal-body">
+        <div class="container-fluid">
+          <div class="row">

+            <div class="col-md-4" style="min-height: 50vh; max-height: 72vh;">

+                <input [(ngModel)]="selectedAttribute"

+                      [typeahead]="typeAheadValues"

+                      (typeaheadOnSelect)="typeaheadOnSelect($event)"

+                      typeaheadOptionField="label"

+                      typeaheadGroupField="group"

+                      typeaheadOptionsLimit="15"

+                      placeholder="Suchtext eingeben"

+                      class="form-control"

+                      style="margin-bottom: 15px;">

+              <searchattribute-tree [searchAttributes]="searchAttributes" [environments]="environments"></searchattribute-tree>

+            </div>

+            <div class="col-md-8" style="min-height: 50vh; max-height: 72vh; overflow-y:auto;">

+            <label for="search-source" class="control-label" style="padding: 8px 0 15px 0;">{{LblSelectedAttributes}}</label>

+            <div *ngIf="currentView.columns.length === 0" style="text-align: center;">

+              Keine Attribute ausgewählt.

+            </div>

+            <table class="table table-bordered" style="padding: 0; overflow-y:auto; table-layout: fixed;">

+              <tr *ngFor="let col of currentView.columns">

+                <td style="width: 50%;" >

+                  {{col.type}}

+                </td>

+                <td style="width: 50%;" >

+                   {{col.name}}

+                </td>

+                <td style="width: 30px; text-align: center; vertical-align: middle;">

+                  <span class="glyphicon glyphicon-menu-up" [ngStyle]="{'visibility': isFirst(col) ? 'hidden': 'visible'}" style="cursor: pointer; margin-bottom: 0;" (click)="moveUp(col)"></span>

+                </td>

+                <td style="width: 30px; text-align: center; vertical-align: middle;">

+                  <span class="glyphicon glyphicon-menu-down" [ngStyle]="{'visibility': isLast(col)? 'hidden': 'visible'}" style="cursor: pointer;" (click)="moveDown(col)"></span>

+                </td>

+                <td style="width: 30px; text-align: center; vertical-align: middle;">

+                  <span class="glyphicon" [ngClass]="{'glyphicon-sort': isNone(col), 'glyphicon-sort-by-alphabet-alt': isDesc(col), 'glyphicon-sort-by-alphabet': isAsc(col)}" style="cursor: pointer;" (click)="toggleSort(col)"></span>

+                </td>

+                <td style="width: 30px; text-align: center; vertical-align: middle;">

+                  <span class="glyphicon glyphicon-remove" [ngClass]="" style="cursor: pointer;" (click)="remove(col)"></span>

+                </td>

+              </tr>

+            </table>

+            </div>

+          </div>

+          <div class="row" style="margin-top: 15px;">

+            <div class="col-md-12">

+              <button type="button" class="btn btn-default pull-right" (click)="applyChanges()">

+                <span class="glyphicon glyphicon-ok"></span> {{LblApplyChanges}}

+              </button>

+            </div>

+          </div>

+        </div>

+      </div>

+    </div>

+  </div>

+</div>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/editview.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/editview.component.ts
new file mode 100644
index 0000000..0b882da
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/editview.component.ts
@@ -0,0 +1,180 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+
+import {Component, Input, ViewChild, OnInit, OnDestroy, Output, EventEmitter} from '@angular/core';
+

+import {View, ViewColumn, ViewService} from './tableview.service';

+import {NodeService} from '../navigator/node.service';

+import {Node} from '../navigator/node';

+import {SearchService, SearchAttribute} from '../search/search.service';

+import {SearchattributeTreeComponent} from '../searchattribute-tree/searchattribute-tree.component';

+import {MDMNotificationService} from '../core/mdm-notification.service';

+

+import {TreeNode} from 'primeng/primeng';
+import {ModalDirective} from 'ng2-bootstrap';
+import {TypeaheadMatch} from 'ng2-bootstrap/typeahead';

+import {Observable} from 'rxjs/Observable';

+import {classToClass} from 'class-transformer';

+

+@Component({

+  selector: 'edit-view',

+  templateUrl: './editview.component.html',

+  styles: ['.remove {color:black; cursor: pointer; float: right}', '.icon { cursor: pointer; margin: 0px 5px; }']

+})

+export class EditViewComponent implements OnInit {

+

+  readonly LblApplyChanges = 'Änderungen anwenden';

+  readonly LblViewEditor = 'Ansichtseditor';

+  readonly LblSelectedAttributes = 'Ausgewählte Attribute';

+

+  @ViewChild('lgModal') public childModal: ModalDirective;

+  @ViewChild(SearchattributeTreeComponent) tree: SearchattributeTreeComponent;

+

+  environments: Node[] = [];

+  isReadOnly = false;

+  currentView: View = new View();

+  searchAttributes: { [env: string]: SearchAttribute[] } = {};

+  typeAheadValues: {label: string, group: string, attribute: SearchAttribute }[] = [];

+

+  selectedAttribute: SearchAttribute;

+

+  @Output()

+  coloumnsSubmitted = new EventEmitter<View>();

+

+  constructor(private nodeService: NodeService,

+    private viewService: ViewService,

+    private searchService: SearchService,

+    private notificationService: MDMNotificationService) { }

+

+  ngOnInit() {

+    this.tree.onNodeSelect$.subscribe(node => this.selectNode(node));

+

+    this.nodeService.getNodes()

+      .subscribe(

+        envs => {

+          this.searchService.loadSearchAttributesStructured(envs.map(e => e.sourceName))

+            .map(attrs => attrs['measurements'])

+            .subscribe(attrs => this.refreshTypeAheadValues(attrs, envs));

+      });

+  }

+

+  refreshTypeAheadValues(searchAttributes: { [env: string]: SearchAttribute[] }, environments: Node[]) {

+

+    this.searchAttributes = searchAttributes;

+    this.environments = environments;

+

+    let ar = Object.keys(this.searchAttributes)

+        .map(env => this.searchAttributes[env])

+        .reduce((acc, value) => acc.concat(value), <SearchAttribute[]> [])

+        .map(sa => { return { 'label': sa.boType + '.' + sa.attrName, 'group': sa.boType, 'attribute': sa }; });

+

+    this.typeAheadValues = this.uniqueBy(ar, p => p.label);

+  }

+

+  showDialog(currentView: View) {

+    this.currentView = classToClass(currentView);

+    this.isNameReadOnly();

+    this.childModal.show();

+    return this.coloumnsSubmitted;

+  }

+

+  closeDialog() {

+    this.childModal.hide();

+  }

+

+  remove(col: ViewColumn) {

+    this.currentView.columns = this.currentView.columns.filter(c => c !== col);

+  }

+

+  isLast(col: ViewColumn) {

+    return this.currentView.columns.indexOf(col) === this.currentView.columns.length - 1;

+  }

+

+  isFirst(col: ViewColumn) {

+    return this.currentView.columns.indexOf(col) === 0;

+  }

+

+  moveUp(col: ViewColumn) {

+    if (!this.isFirst(col)) {

+      let oldIndex = this.currentView.columns.indexOf(col);

+      let otherCol = this.currentView.columns[oldIndex - 1];

+      this.currentView.columns[oldIndex] = otherCol;

+      this.currentView.columns[oldIndex - 1] = col;

+    }

+  }

+

+  moveDown(col: ViewColumn) {

+    if (!this.isLast(col)) {

+      let oldIndex = this.currentView.columns.indexOf(col);

+      let otherCol = this.currentView.columns[oldIndex + 1];

+      this.currentView.columns[oldIndex] = otherCol;

+      this.currentView.columns[oldIndex + 1] = col;

+    }

+  }

+

+  selectNode(node: TreeNode) {
+    if (node.type !== 'attribute') {

+      return;

+    }

+    this.pushViewCol(new ViewColumn(node.parent.label, node.label));

+  }

+

+  pushViewCol(viewCol: ViewColumn) {

+    if (viewCol && this.currentView.columns.findIndex(c => viewCol.equals(c)) === -1 ) {

+      this.currentView.columns.push(viewCol);

+    } else {

+      this.notificationService.notifyInfo('Das Attribut wurde bereits ausgewählt!', 'Info');

+    }

+  }

+

+  isAsc(col: ViewColumn) {

+    return col.sortOrder === 1;

+  }

+  isDesc(col: ViewColumn) {

+    return col.sortOrder === -1;

+  }

+  isNone(col: ViewColumn) {

+    return col.sortOrder === null;

+  }

+

+  toggleSort(col: ViewColumn) {

+    if (col.sortOrder === null) {

+      this.currentView.setSortOrder(col.type, col.name, 1);

+    } else if (col.sortOrder === 1) {

+      this.currentView.setSortOrder(col.type, col.name, -1);

+    } else if (col.sortOrder === -1) {

+      this.currentView.setSortOrder(col.type, col.name, null);

+    }

+  }

+

+  private uniqueBy<T>(a: T[], key: (T) => any) {

+    let seen = {};

+    return a.filter(function(item) {

+      let k = key(item);

+      return seen.hasOwnProperty(k) ? false : (seen[k] = true);

+    });

+  }

+

+  public typeaheadOnSelect(match: TypeaheadMatch) {

+    this.pushViewCol(new ViewColumn(match.item.attribute.boType, match.item.attribute.attrName));

+    this.selectedAttribute = undefined;

+  }

+

+  private isNameReadOnly() {

+    return this.isReadOnly = (this.currentView.name === '') ? false : true;

+  }

+

+  applyChanges() {

+    this.coloumnsSubmitted.emit(this.currentView);

+    this.closeDialog();

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/query.service.spec.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/query.service.spec.ts
new file mode 100644
index 0000000..6d9050b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/query.service.spec.ts
@@ -0,0 +1,85 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { DebugElement } from '@angular/core';

+import { By } from '@angular/platform-browser';

+import { Observable } from 'rxjs/Observable';

+

+import { ComponentFixture, async, TestBed, inject } from '@angular/core/testing';

+import { BaseRequestOptions, Http, HttpModule, Response, ResponseOptions, RequestMethod} from '@angular/http';

+import { MockBackend, MockConnection } from '@angular/http/testing';

+

+import { MDMItem } from '../core/mdm-item';

+import { PropertyService } from '../core/property.service';

+import { QueryService, Query } from './query.service';

+

+describe ( 'QueryService', () => {

+

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        PropertyService,

+        QueryService,

+        MockBackend,

+        BaseRequestOptions,

+        {

+          provide: Http,

+          useFactory: (backend, options) => new Http(backend, options),

+          deps: [MockBackend, BaseRequestOptions]

+        }]

+    });

+  });

+

+  describe('query()', () => {

+    it('should return result for simple query', async(inject([QueryService, MockBackend], (queryService, mockBackend) => {

+      mockBackend.connections.subscribe((conn: MockConnection) => {

+        if (conn.request.url.endsWith('/query') && conn.request.method === RequestMethod.Post) {

+          let mockResponse = { rows: [

+            {source: 'MDMNVH', type: 'Test', id: 1, columns: [{type: 'Test', attribute: 'Name', value: 'TestNumberOne'}]}

+          ]};

+          conn.mockRespond(new Response(new ResponseOptions({ body: mockResponse })));

+        }

+        return;

+      });

+

+      queryService.query(new Query()).subscribe(results => {

+        expect(results.rows.length).toBe(1);

+        expect(results.rows[0].columns[0].value).toEqual('TestNumberOne');

+      });

+    })));

+  });

+

+  describe('queryItems()', () => {

+    it('should return result for simple query', async(inject([QueryService, MockBackend], (queryService, mockBackend) => {

+      mockBackend.connections.subscribe((conn: MockConnection) => {

+        if (conn.request.url.endsWith('/query') && conn.request.method === RequestMethod.Post) {

+          let queryObject = <Query>JSON.parse(conn.request.getBody());

+          let mockResponse = { rows: [

+            {source: 'MDMNVH', type: queryObject.resultType, id: 1, columns: [{type: 'Test', attribute: 'Name', value: 'TestNumberOne'}]}

+          ]};

+          conn.mockRespond(new Response(new ResponseOptions({ body: mockResponse })));

+        }

+        return;

+      });

+      let item = new MDMItem('MDMNVH', 'Test', 1);

+

+      let result = queryService.queryItems([item], ['Test.Name']);

+      expect(result.length).toBe(1);

+      result[0].subscribe(results => {

+        expect(results.rows.length).toBe(1);

+        expect(results.rows[0].type).toEqual('Test');

+        expect(results.rows[0].columns[0].value).toEqual('TestNumberOne');

+      });

+    })));

+  });

+});

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/query.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/query.service.ts
new file mode 100644
index 0000000..b8988ae
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/query.service.ts
@@ -0,0 +1,142 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import {Injectable, EventEmitter} from '@angular/core';

+import {Http, Response, Headers, RequestOptions} from '@angular/http';

+import { Observable } from 'rxjs/Observable';

+

+import {PropertyService} from '../core/property.service';

+import {MDMItem} from '../core/mdm-item';

+import {Type, Exclude, plainToClass, serialize, deserialize} from 'class-transformer';

+

+export class Filter {

+  sourceName: string;

+  filter: string;

+  searchString: string;

+

+  constructor(sourceName: string, filter: string, searchString: string) {

+    this.sourceName = sourceName;

+    this.filter = filter;

+    this.searchString = searchString;

+  }

+}

+export class Query {

+  resultType: string;

+  @Type(() => Filter)

+  filters: Filter[] = [];

+  columns: String[] = [];

+

+  addFilter(sourceName: string, filter: string) {

+    let f = this.filters.find(i => i.sourceName === sourceName);

+    if (f) {

+      f.filter += ' or ' + filter; // TODO

+    } else {

+      this.filters.push(new Filter(sourceName, filter, ''));

+    }

+  }

+}

+

+export class Columns {

+  type: string;

+  attribute: string;

+  value: string;

+}

+

+export class Row {

+  source: string;

+  type: string;

+  id: string;

+  @Type(() => Columns)

+  columns: Columns[] = [];

+

+  getColumn(col: string) {

+    let column = this.columns.find(c => c.type + '.' + c.attribute === col);

+    if (column) {

+      return column.value;

+    } else {

+      return '';

+    }

+  }

+

+  equals (row: Row) {

+    return this.source === row.source && this.type === row.type && this.id === row.id;

+  }

+

+  public getItem() {

+    return new MDMItem(this.source, this.type, +this.id);

+  }

+}

+

+export class SearchResult {

+  @Type(() => Row)

+  rows: Row[] = [];

+}

+

+@Injectable()

+export class QueryService {

+  private queryUrl: string;

+

+  constructor(private http: Http,

+              private _prop: PropertyService) {

+    this.queryUrl = _prop.getUrl('/mdm/query');

+  }

+

+  query(query: Query): Observable<SearchResult> {

+    return this.http.post(this.queryUrl, query)

+               .map(res => deserialize(SearchResult, res.text()))

+               .catch(this.handleError);

+  }

+

+  queryItems(items: MDMItem[], columns: string[]): Observable<SearchResult>[] {

+    let byType = items.reduce((acc: [string, MDMItem[]], item: MDMItem) => {

+      let key = item.type;

+      acc[key] = acc[key] || [];

+      acc[key].push(item);

+      return acc;

+    }, {});

+

+    return Object.keys(byType).map(type => this.queryType(type, byType[type], columns));

+  }

+

+  queryType(type: string, items: MDMItem[], columns: string[]) {

+    if (items && items.length > 0) {

+      let query = new Query();

+      query.resultType = type;

+      query.columns = columns;

+

+      query.columns.push(type + '.Id');

+      items.forEach(i => query.addFilter(i.source, i.type + '.Id eq ' + i.id));

+

+      return this.query(query);

+    } else {

+      return Observable.of(new SearchResult());

+    }

+  }

+

+  suggestValues(environments: string[], type: string, attribute: string) {

+    let body = JSON.stringify({

+      'sourceNames': environments,

+      'type': type,

+      'attrName': attribute

+    });

+    let headers = new Headers({ 'Content-Type': 'application/json' });

+    let options = new RequestOptions({ headers: headers });

+    let url =  this._prop.getUrl('/mdm/suggestions');

+    return this.http.post(url, body, options)

+      .map(res => res.json().data);

+  }

+

+  private handleError(error: Response) {

+    console.error(error);

+    return Observable.throw(error.json().error || 'Server error');

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.css b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.css
new file mode 100644
index 0000000..f115d86
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.css
@@ -0,0 +1,31 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+.table > tbody > tr.active > td {

+	background-color: #66afe9; /*#337ab7;*/

+}

+

+.noselect {

+  -webkit-touch-callout: none; /* iOS Safari */

+    -webkit-user-select: none; /* Safari */

+     -khtml-user-select: none; /* Konqueror HTML */

+       -moz-user-select: none; /* Firefox */

+        -ms-user-select: none; /* Internet Explorer/Edge */

+            user-select: none; /* Non-prefixed version, currently

+                                  supported by Chrome and Opera */

+}

+

+>>> thead >>> .ui-state-active {

+	background-color: #e7e7e7 !important;

+	border-color: #c7c7c7 !important;

+	color: inherit !important;

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.html
new file mode 100644
index 0000000..662337f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.html
@@ -0,0 +1,58 @@
+<!-- ***************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+**************************************************************************** -->
+<style>
+:host >>> .fixtable { table-layout: auto;}
+</style>
+
+<div style="overflow-x: auto;">
+
+<p-contextMenu #cm [model]="menuItems" appendTo="body"></p-contextMenu>
+
+<p-dataTable
+  tableStyleClass="fixtable"
+  [value]="results?.rows"
+  resizableColumns="true"
+  columnResizeMode="expand"
+  [reorderableColumns]="true"
+  [rows]="10"
+  [paginator]="true"
+  [pageLinks]="3"
+  [rowsPerPageOptions]="[10,20,50,100,200,500,1000]"
+  [(selection)]="selectedRows"
+  [contextMenu]="cm"
+  [sortField]="view?.getSortField()"
+  [sortOrder]="view?.getSortOrder()"
+  (onRowClick)="onRowClick($event)"
+  (onColResize)="onColResize($event)"
+  (onColReorder)="onColReorder($event)"
+  (onSort)="onSort($event)"
+  (onContextMenuSelect)="onContextMenuSelect($event)">
+
+  <p-column [style]="buttonColStyle" selectionMode="multiple" [hidden]="btnColHidden"></p-column>
+  <p-column [style]="buttonColStyle" [hidden]="btnColHidden">
+    <template pTemplate="body" let-col let-row="rowData">
+      <a class="icon" [ngClass]="getNodeClass(row.type)" style="color:black; cursor: pointer;" (click)="openInTree(row, $event)" title="{{getRowTitle(row)}}"> </a>
+    </template>
+  </p-column>
+  <p-column [style]="buttonColStyle" [hidden]="btnColHidden">
+    <template pTemplate="body" let-col let-row="rowData" >
+      <span class="glyphicon" [ngClass]="{'glyphicon-shopping-cart': isShopable, 'glyphicon-remove': isRemovable}"
+        style="cursor: pointer" title="{{getIconTitle()}}" (click)="functionalityProvider(isShopable, row)"></span>
+    </template>
+  </p-column>
+  <p-column *ngFor="let col of view?.columns" [style]="col.style" header="{{col.type}}.{{col.name}}" sortable="custom" sortField="{{col.type}}.{{col.name}}" (sortFunction)="customSort($event)" [hidden]="col.hidden">
+    <template pTemplate="body" let-col let-row="rowData" >
+      {{row.getColumn(col.header)}}
+    </template>
+  </p-column>
+</p-dataTable>
+</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.ts
new file mode 100644
index 0000000..32dbe6c
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.ts
@@ -0,0 +1,234 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { Component, Input, ViewChild, OnInit, OnChanges, SimpleChanges} from '@angular/core';

+

+import { View, ViewColumn} from './tableview.service';

+import { NavigatorService } from '../navigator/navigator.service';

+import { SearchAttribute } from '../search/search.service';

+import { BasketService} from '../basket/basket.service';

+

+import { EditViewComponent } from './editview.component';

+import { SearchResult, Row } from './query.service';

+import { Node } from '../navigator/node';

+import { NodeService } from '../navigator/node.service';

+

+import {DataTableModule, SharedModule, ContextMenuModule, MenuItem} from 'primeng/primeng';

+

+@Component({

+  selector: 'mdm-tableview',

+  templateUrl: 'tableview.component.html',

+  styleUrls: ['./tableview.component.css']

+})

+export class TableviewComponent implements OnInit, OnChanges {

+

+  public static readonly pageSize = 5;

+  readonly buttonColumns = 3;

+

+  readonly TtlAddToBasket = 'Zum Warenkorb hinzufügen';

+  readonly TtlOpenInTree = 'Öffnen in';

+  readonly TtlRemoveFromBasket = 'Aus dem Warenkorb entfernen';
+

+  @Input() view: View;

+  @Input() results: SearchResult;

+  @Input() isShopable = false;

+  @Input() isRemovable = false;

+  @Input() menuItems: MenuItem[] = [];

+  @Input() selectedEnvs: Node[];

+  @Input() searchAttributes: { [env: string]: SearchAttribute[] };

+  @Input() environments: Node[];

+

+  public menuSelectedRow: Row;

+  public selectedRows: Row[] = [];

+  public columnsToShow: ViewColumn[];

+  public readonly buttonColStyle = {'width': '3%'};

+  public btnColHidden = false;

+

+  constructor(private basketService: BasketService,

+    private navigatorService: NavigatorService,

+    private nodeService: NodeService) {

+  }

+

+  ngOnInit() {

+    this.menuItems.push({

+      label: 'Im Baum zeigen',

+      icon: 'glyphicon glyphicon-tree-conifer',

+      command: (event) => this.openInTree()

+    });

+    this.menuItems.push({

+      label: 'Selektion zurücksetzen',

+      icon: 'glyphicon glyphicon-unchecked',

+      command: (event) => this.selectedRows = []

+    });

+  }

+

+  ngOnChanges(changes: SimpleChanges) {

+    if (changes['results'] && this.view) {

+        this.customSort({ 'field': this.view.getSortField(), 'order': this.view.getSortOrder() });

+    }

+    if (changes['selectedEnvs'] || changes['view']) {

+      this.updateColumnsToShow();

+    }

+  }

+

+  updateColumnsToShow() {

+    if (this.view && this.selectedEnvs && this.selectedEnvs.length > 0 && this.searchAttributes[this.selectedEnvs[0].sourceName]) {

+      let relevantCols: string[] = [];

+      this.selectedEnvs.forEach(env =>

+        relevantCols = relevantCols.concat(this.searchAttributes[env.sourceName].map(sa =>
+          sa.boType.toLowerCase() + sa.attrName.toLowerCase())
+        )

+      );

+      relevantCols = Array.from(new Set(relevantCols));

+

+      this.view.columns.filter(col => {

+        if (relevantCols.findIndex(ct => ct === col.type.toLowerCase() + col.name.toLowerCase()) > -1) {

+          col.hidden = false;

+        } else {

+          col.hidden = true;

+        }

+        this.btnColHidden = false;

+      });

+    } else if (this.view && this.selectedEnvs && this.selectedEnvs.length === 0) {

+      this.btnColHidden = true;

+      this.view.columns.forEach(vc => vc.hidden = true);

+    }

+  }

+

+  onContextMenuSelect(event: any) {

+    this.menuSelectedRow = event.data;

+  }

+

+  onColResize(event: any) {

+    let index = event.element.cellIndex - this.buttonColumns;

+    if (index > -1) {

+      this.view.columns[index].style = { 'width': event.element.clientWidth.toString() + 'px' };

+    }

+  }

+

+  onColReorder(event: any) {

+    let dragIndex = event.dragIndex > this.buttonColumns ? event.dragIndex - this.buttonColumns : 0;

+    let dropIndex = event.dropIndex > this.buttonColumns ? event.dropIndex - this.buttonColumns : 0;

+

+    let tmp = this.view.columns[dragIndex];

+    this.view.columns[dragIndex] = this.view.columns[dropIndex];

+    this.view.columns[dropIndex] = tmp;

+  }

+

+  onSort(event: any) {

+    let a = event.field.split('.');

+    this.view.setSortOrder(a[0], a[1], event.order);

+  }

+

+  customSort(event: any) {

+    let comparer = function(row1: Row, row2: Row): number {

+      let value1 = row1.getColumn(event.field) || '';

+      let value2 = row2.getColumn(event.field) || '';

+

+      if (!isNaN(<any>value1) && !isNaN(<any>value2)) {

+        if (value1 === value2) {

+          return 0;

+        } else {

+          return (+value1 < +value2 ? -1 : 1) * event.order;

+        }

+      } else {

+        return value1.localeCompare(value2) * -event.order;

+      }

+    };

+

+    this.results.rows.sort(comparer);

+  }

+

+  functionalityProvider(isShopable: boolean, row: Row) {

+    let item = row.getItem();

+    if (isShopable) {

+      this.basketService.add(item);

+    } else {

+      this.basketService.remove(item);

+    }

+  }

+

+  getNodeClass(type: string) {

+    switch (type) {

+      case 'StructureLevel':

+        return 'pool';

+      case 'MeaResult':

+        return 'measurement';

+      case 'SubMatrix':

+        return 'channelgroup';

+      case 'MeaQuantity':

+        return 'channel';

+      default:

+        return type.toLowerCase();

+    }

+  }

+

+  getRowTitle(row: Row) {

+    return this.TtlOpenInTree + ': ' + [NodeService.mapSourceNameToName(this.environments, row.source), row.type, row.id].join('/');

+  }

+

+  getIconTitle() {

+    return this.isShopable ? this.TtlAddToBasket : this.TtlRemoveFromBasket;

+  }

+  onRowClick(e: any) {

+    let row: Row = e.data;

+    this.nodeService.getNodeFromItem(row.getItem()).subscribe(

+      node => this.navigatorService.fireSelectedNodeChanged(node)

+    );

+    let event: MouseEvent = e.originalEvent;

+    if (event.shiftKey && this.selectedRows.length > 0) {

+      let lastRow = this.selectedRows[this.selectedRows.length - 1];

+      let lastIndex = this.results.rows.findIndex(r => r.equals(lastRow));

+      let thisIndex = this.results.rows.findIndex(r => r.equals(row));

+      if (this.selectedRows.findIndex(sr => sr.equals(row)) > -1) {

+      } else {

+        let min = Math.min(lastIndex, thisIndex);

+        let max = Math.max(lastIndex, thisIndex);

+        this.results.rows.slice(min, max + 1)

+          .forEach(r => {

+            if (this.selectedRows.findIndex(sr => sr.equals(r)) === -1) {

+              this.selectedRows.push(r);

+            }

+          });

+      }

+    } else if (event.ctrlKey) {

+      this.selectRow(row);

+    } else {

+      if (this.selectedRows.length > 1 || (this.selectedRows.length !== 0 && !row.equals(this.selectedRows[0]))) {

+        this.selectedRows = [];

+      }

+      this.selectRow(row);

+    }

+  }

+

+  selectRow(row: Row) {

+    let index = this.selectedRows.findIndex(ai => ai.equals(row));

+    if (index === -1) {

+      this.selectedRows.push(row);

+    } else {

+      this.selectedRows.splice(index, 1);

+    }

+  }

+

+  openInTree(row?: Row) {

+    if (row) {

+      this.selectedRows = [row];

+    }

+    if (this.selectedRows && this.selectedRows.length === 0 && this.menuSelectedRow) {

+      this.navigatorService.fireOnOpenInTree([this.menuSelectedRow.getItem()]);

+    } else if (row) {

+      this.navigatorService.fireOnOpenInTree([row.getItem()]);

+    } else {

+      this.navigatorService.fireOnOpenInTree(this.selectedRows.map(r => r.getItem()));

+    }

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.module.ts
new file mode 100644
index 0000000..c6e075d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.module.ts
@@ -0,0 +1,45 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { NgModule } from '@angular/core';

+

+import { MDMCoreModule } from '../core/mdm-core.module';

+

+import { TableviewComponent } from './tableview.component';

+import { EditViewComponent } from './editview.component';

+import { ViewComponent } from './view.component';

+import {SearchattributeTreeModule} from '../searchattribute-tree/searchattribute-tree.module';

+

+import {DataTableModule, SharedModule} from 'primeng/primeng';

+

+@NgModule({

+  imports: [

+    MDMCoreModule,

+    SearchattributeTreeModule,

+    DataTableModule,

+    SharedModule

+  ],

+  declarations: [

+    TableviewComponent,

+    EditViewComponent,

+    ViewComponent

+  ],

+  exports: [

+    TableviewComponent,

+    EditViewComponent,

+    ViewComponent,

+  ],

+  providers: [

+  ]

+})

+export class TableViewModule {

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.service.spec.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.service.spec.ts
new file mode 100644
index 0000000..bfd8823
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.service.spec.ts
@@ -0,0 +1,83 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { DebugElement } from '@angular/core';

+import { Observable } from 'rxjs/Observable';

+

+import { ComponentFixture, async, TestBed, inject } from '@angular/core/testing';

+import { BaseRequestOptions, Http, HttpModule, Response, ResponseOptions } from '@angular/http';

+import { MockBackend } from '@angular/http/testing';

+

+import { ViewService } from './tableview.service';

+import { PropertyService } from '../core/property.service';

+import { PreferenceService, Scope } from '../core/preference.service';

+

+describe ( 'TableviewService', () => {

+

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        ViewService,

+        PropertyService,

+        PreferenceService,

+        MockBackend,

+        BaseRequestOptions,

+        {

+          provide: Http,

+          useFactory: (backend, options) => new Http(backend, options),

+          deps: [MockBackend, BaseRequestOptions]

+        }]

+    });

+  });

+

+  describe('getViews()', () => {

+    it('should return view from preference', async(inject([ViewService, MockBackend], (tableviewService, mockBackend) => {

+

+      mockBackend.connections.subscribe(conn => {

+        const mockResponse = {

+          preferences: [

+          {

+            id: 22,

+            key: 'tableview.view.Test',

+            scope: Scope.USER,

+            source: null,

+            user: 'sa',

+            value: '{"columns":[{"type":"Test","name":"Name"},{"type":"TestStep","name":"Name"}],"name":"Test"}'

+          }

+        ]};

+        conn.mockRespond(new Response(new ResponseOptions({ body: mockResponse })));

+      });

+

+      tableviewService.getViews().subscribe(prefViews => {

+        expect(prefViews.length).toBe(1);

+        expect(prefViews[0].scope).toBe(Scope.USER);

+        expect(prefViews[0].view.columns.length).toBe(2);

+      });

+    })));

+

+    it('should return default view, if no view preferences are available',

+        async(inject([ViewService, MockBackend], (tableviewService, mockBackend) => {

+

+      mockBackend.connections.subscribe(conn => {

+        const mockResponse = { preferences: [] };

+        conn.mockRespond(new Response(new ResponseOptions({ body: mockResponse })));

+      });

+

+      tableviewService.getViews().subscribe(prefViews => {

+        expect(prefViews.length).toBe(1);

+        expect(prefViews[0].scope).toBe(Scope.SYSTEM);

+        expect(prefViews[0].view.columns.length).toBe(1);

+      });

+    })));

+  });

+});

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.service.ts
new file mode 100644
index 0000000..b78cb2d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.service.ts
@@ -0,0 +1,121 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { Injectable, EventEmitter } from '@angular/core';

+import { Observable } from 'rxjs/Observable';

+

+import { PreferenceService, Preference, Scope } from '../core/preference.service';
+import { Type, Exclude, plainToClass, serialize, deserialize } from 'class-transformer';
+

+export class View {

+  name: string;

+  @Type(() => ViewColumn)

+  columns: ViewColumn[] = [];

+

+  constructor(name?: string, cols?: ViewColumn[]) {

+    this.name = name || 'Neue Ansicht';

+    this.columns = cols || [];

+  }

+

+  setSortOrder(type: string, name: string, order: any) {

+    this.columns.forEach(c => {

+    if (c.type === type && c.name === name ) {

+      c.sortOrder = order;

+    } else {

+      c.sortOrder = null;

+    }

+  });

+  }

+

+  getSortOrder() {

+    let col = this.columns.find(c => c.sortOrder !== null);

+    if (col) {

+      return col.sortOrder;

+    }

+  }

+

+  getSortField() {

+    let col = this.columns.find(c => c.sortOrder !== null);

+    if (col) {

+      return col.type + '.' + col.name;

+    }

+  }

+}

+

+export class PreferenceView {

+  scope: string;

+  @Type(() => View)

+  view: View;

+

+  constructor(scope: string, view?: View) {

+    this.scope = scope;

+    this.view = view || new View();

+  }

+}

+

+export class Style {

+  [field: string]: string

+}

+

+export class ViewColumn {

+  type: string;

+  name: string;

+  @Type(() => Style)

+  style: Style;

+  hidden = false;

+  sortOrder: number;

+

+  constructor(type: string, name: string, style?: Style, sortOrder?: number) {

+    this.type = type;

+    this.name = name;

+    this.style = style || undefined;

+    this.sortOrder = sortOrder || null;

+  }

+

+  equals(vc: ViewColumn) {

+    return this.type === vc.type && this.name === vc.name;

+  }

+}

+

+@Injectable()

+export class ViewService {

+  public viewSaved$ = new EventEmitter<View>();

+  public viewDeleted$ = new EventEmitter();

+

+  readonly preferencePrefix = 'tableview.view.';

+  private views: View[] = [];

+

+  defaultPrefViews =  [ new PreferenceView(Scope.SYSTEM, new View('Standard', [new ViewColumn('Test', 'Name')])) ];

+

+  constructor(private prefService: PreferenceService) {

+  }

+

+  getViews() {

+    return this.prefService.getPreference(this.preferencePrefix)

+        .map(preferences => preferences.map(p => new PreferenceView(p.scope, deserialize(View, p.value))))

+        .filter(prefViews => !(prefViews === undefined || prefViews.length === 0))

+        .defaultIfEmpty(this.defaultPrefViews);

+  }

+

+  saveView(view: View) {
+    const pref = new Preference();

+    pref.value = serialize(view);

+    pref.key = this.preferencePrefix + view.name;

+    pref.scope = Scope.USER;

+    this.prefService.savePreference(pref).subscribe(saved => this.viewSaved$.emit(view));

+  }
+
+  deleteView(name: string) {
+    return this.prefService.deletePreferenceByScopeAndKey(Scope.USER, 'tableview.view.' + name);
+  }
+
+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/view.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/view.component.html
new file mode 100644
index 0000000..0c33692
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/view.component.html
@@ -0,0 +1,101 @@
+<!-- ***************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+**************************************************************************** -->

+

+<style>

+   :host /deep/ .dropdown {

+    width: 200px;

+  }

+

+   :host /deep/ .dropdown-toggle {

+    overflow: hidden;

+    padding-right: 24px/* Optional for caret */

+    ;

+    text-align: left;

+    text-overflow: ellipsis;

+    width: 100%;

+  }

+  /* Optional for caret */

+

+   :host /deep/ .dropdown-toggle .caret {

+    position: absolute;

+    right: 12px;

+    top: calc(50% - 2px);

+  }

+</style>

+

+<div class="btn-group" dropdown>

+  <button id="view" title="{{TtlSelectView}}" type="button" class="btn btn-default dropdown-toggle" dropdownToggle aria-haspopup="true" aria-expanded="false">

+    {{selectedView?.name}} <span class="caret"></span>

+  </button>

+  <ul class="dropdown-menu scrollable-menu" style="height: auto; max-height: 200px; overflow-x: hidden;" dropdownMenu>

+    <div *ngFor="let groupedView of groupedViews" class="container-fluid">

+      <li class="dropdown-header" style="padding-left: 0pt;"> {{groupedView.label}} </li>

+      <li *ngFor="let view of groupedView.view">

+        <a class="dropdown-item" style="color:black; cursor: pointer;" (click)="selectView(view)">{{view.name}}</a>

+      </li>

+    </div>

+  </ul>

+</div>

+<button type="button" class="btn btn-default" (click)="newView($event)" title="{{TtlNewView}}"><span class="glyphicon glyphicon-plus"></span></button>

+<button type="button" class="btn btn-default" (click)="editSelectedView($event)" title="{{TtlEditView}}"><span class="glyphicon glyphicon-edit"></span></button>

+<button type="button" class="btn btn-default" (click)="showSaveModal($event)" title="{{TtlSaveView}}"><span class="fa fa-floppy-o"></span></button>

+<button type="button" class="btn btn-default" (click)="deleteView($event)" title="{{TtlDeleteView}}"><span class="glyphicon glyphicon-remove"></span></button>

+

+<edit-view></edit-view>

+<overwrite-dialog></overwrite-dialog>

+

+<div bsModal #lgSaveModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="SelectSearchComponents" aria-hidden="true">

+  <div class="modal-dialog modal-md">

+    <div class="modal-content">

+      <div class="modal-header">

+        <button type="button" class="close" (click)="childSaveModal.hide()" aria-label="Close">

+          <span aria-hidden="true">&times;</span>

+        </button>

+        <h4 class="modal-title">{{LblSaveViewAs}}:</h4>

+      </div>

+      <div class="modal-body">

+        <div class="container-fluid">

+          <div class="row" *ngIf="userViewNames?.length > 0">

+            <p-dataTable

+              [value]="userViewNames"

+              resizableColumns="false"

+              [reorderableColumns]="false"

+              [rows]="10"

+              [paginator]="true"

+              [pageLinks]="3"

+              [rowsPerPageOptions]="[10,20,50]"

+              [(selection)]="selectedRow"

+              (onRowClick)="onRowSelect($event)"

+              (onRowSelect)="onRowSelect($event)">

+              <p-column [style]="{'width':'30px'}" selectionMode="single"></p-column>

+              <p-column header="{{LblExistingUserNames}}">

+                <template pTemplate="body" let-col let-row="rowData" >

+                  {{row}}

+                </template>

+              </p-column>

+            </p-dataTable>

+          </div>

+          <div class="row" style="margin-top: 15px;">

+            <div class="col-md-10" style="padding-left: 0;">

+              <input type="text" class="form-control" placeholder="Ansicht-Name" [value]="viewName" (input)="viewName = $event.target.value" (keyup.enter)="saveView($event)" required>

+            </div>

+            <div class="col-md-2" style="padding: 0;">

+                <button type="button" class="btn btn-default pull-right" (click)="saveView($event)" [disabled]="!viewName" title="{{getSaveBtnTitle()}}">

+                  <span class="fa fa-floppy-o"></span> {{LblSave}}

+                </button>

+            </div>

+          </div>

+        </div>

+      </div>

+    </div>

+  </div>

+</div>

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/view.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/view.component.ts
new file mode 100644
index 0000000..4fd70ed
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/view.component.ts
@@ -0,0 +1,191 @@
+/*******************************************************************************

+*  Copyright (c) 2017 Peak Solution GmbH                                       *

+*                                                                              *

+*  All rights reserved. This program and the accompanying materials            *

+*  are made available under the terms of the Eclipse Public License v1.0       *

+*  which accompanies this distribution, and is available at                    *

+*  http://www.eclipse.org/legal/epl-v10.html                                   *

+*                                                                              *

+*  Contributors:                                                               *

+*  Matthias Koller, Johannes Stamm - initial implementation                    *

+*******************************************************************************/

+

+import { Component, ViewChild, OnInit, EventEmitter} from '@angular/core';

+

+import { PreferenceView, View, ViewService} from './tableview.service';

+import { EditViewComponent } from './editview.component';

+

+import {classToClass} from 'class-transformer';

+import { ModalDirective } from 'ng2-bootstrap';

+

+import { OverwriteDialogComponent } from '../core/overwrite-dialog.component';

+import { MDMNotificationService } from '../core/mdm-notification.service';

+import { Scope } from '../core/preference.service';

+@Component({

+  selector: 'mdm-view',

+  templateUrl: 'view.component.html'

+})

+export class ViewComponent implements OnInit {

+

+  readonly LblSave = 'Speichern';

+  readonly LblSaveViewAs = 'Ansicht speichern unter';

+  readonly LblExistingUserNames = 'Vorhandene Ansichtsnamen';

+  readonly TtlDeleteView = 'Ansicht löschen';

+  readonly TtlEditView = 'Ansicht bearbeiten';

+  readonly TtlNewView = 'Neue Ansicht erstellen';

+  readonly TtlNoNameSet = 'Name nicht gesetzt!';

+  readonly TtlSaveView = 'Ansicht speichern';

+  readonly TtlSelectView = 'Ansicht auswählen';

+

+  public selectedView: View;

+  public groupedViews: { scope: string, view: View[], label: string }[] = [];

+  public viewChanged$ = new EventEmitter();

+  public viewName = '';

+  public userViewNames: string[];

+  public selectedRow: string;

+  public lazySelectedRow: string;

+

+  @ViewChild(EditViewComponent)

+  private editViewComponent: EditViewComponent;

+

+  @ViewChild('lgSaveModal')

+  childSaveModal: ModalDirective;

+

+  @ViewChild(OverwriteDialogComponent)

+  private overwriteDialogComponent: OverwriteDialogComponent;

+

+  constructor(private viewService: ViewService,

+    private notificationService: MDMNotificationService) {

+  }

+

+  ngOnInit() {

+    this.viewService.getViews().subscribe(views => this.setViews(views));

+    this.viewService.viewSaved$.subscribe(view => this.onViewSaved(view));

+    this.viewService.viewDeleted$.subscribe(() => this.onDeleteView());

+  }

+

+  selectView(view: View) {

+    this.selectedView = view;

+    this.viewChanged$.emit();

+  }

+

+  public editSelectedView(e: Event) {

+    e.stopPropagation();

+    this.editViewComponent.showDialog(this.selectedView).subscribe(v => this.selectedView = v);

+  }

+

+  public newView(e: Event) {

+    e.stopPropagation();

+    this.editViewComponent.showDialog(new View()).subscribe(v => this.selectedView = v);

+  }

+

+  private onDeleteView() {

+    this.viewService.getViews().subscribe(prefViews => {

+      this.getGroupedView(prefViews);

+      if (prefViews.find(pv => pv.view.name === this.selectedView.name) === undefined

+          && this.viewService.defaultPrefViews.find(pv => pv.view.name === this.selectedView.name) === undefined) {

+        this.selectView(prefViews[0].view);

+      }

+    });

+  }

+

+ private onViewSaved(view: View) {

+   this.viewService.getViews().subscribe(prefViews => this.getGroupedView(prefViews));

+    // if (this.selectedView.name === view.name) {

+    //   this.selectView(classToClass(view));

+    // }

+  }

+

+  private setViews(prefViews: PreferenceView[]) {

+    this.getGroupedView(prefViews);

+    this.selectView(prefViews[0].view);

+  }

+

+  private getGroupedView(prefViews: PreferenceView[]) {

+    this.groupedViews = [];

+    for (let i = 0; i < prefViews.length; i++) {

+      let pushed = false;

+      for (let j = 0; j < this.groupedViews.length; j++) {

+        if (prefViews[i].scope === this.groupedViews[j].scope) {

+          this.groupedViews[j].view.push(prefViews[i].view);

+          pushed = true;

+        }

+      }

+      if (pushed === false) { this.groupedViews.push({

+        scope: prefViews[i].scope,

+        view: [prefViews[i].view],

+        label: Scope.toLabel(prefViews[i].scope)

+      }); }

+    }

+    this.updateUserViewNames();

+  }

+

+  private updateUserViewNames() {

+    this.viewService.getViews().subscribe(prefViews =>

+      this.userViewNames = prefViews.filter(pv => pv.scope === Scope.USER).map(pv => pv.view.name)

+    );

+  }

+

+  saveView(e: Event) {

+    e.stopPropagation();

+    if (this.groupedViews.find(gv => gv.view.find(v => v.name === this.viewName) !== undefined)) {

+      this.childSaveModal.hide();

+      this.overwriteDialogComponent.showOverwriteModal('eine Ansicht').subscribe(ovw => this.saveView2(ovw));

+    } else {

+      this.saveView2(true);

+    }

+  }

+

+  saveView2(save: boolean) {

+    if (save) {

+      let view = classToClass(this.selectedView);

+      view.name = this.viewName;

+      this.viewService.saveView(view);

+      this.childSaveModal.hide();

+      this.selectView(classToClass(view));

+    } else {

+      this.childSaveModal.show();

+    }

+  }

+

+  showSaveModal(e: Event) {

+    e.stopPropagation();

+    this.viewName = this.selectedView.name === 'Neue Ansicht' ? '' : this.selectedView.name;

+    this.childSaveModal.show();

+  }

+

+  deleteView(e: Event) {

+    e.stopPropagation();

+    let userGroup = this.groupedViews.find(gv => gv.scope === Scope.USER);

+    if (userGroup && userGroup.view.length > 0) {

+      this.viewService.deleteView(this.selectedView.name).subscribe(() =>

+        this.viewService.getViews().subscribe(views => {

+          this.setViews(views);

+          this.viewService.viewDeleted$.emit();

+        },

+          () => this.notificationService.notifyError('Server Error.',

+            'Die Ansicht konnte auf Grund eine Server Fehlers nicht gelöscht werden!'))

+      );

+    } else {

+      this.notificationService.notifyError('Forbidden.',

+        'System Einstellungen können nicht aus der Anwendung geändert werden.'

+        + ' Sollten sie entsprechende Zugriffsrechte besitzen,'

+        + ' können Sie diese Einstellungen über den Administrationsbereich bearbeiten');

+    }

+  }

+

+  getSaveBtnTitle() {

+    return this.viewName ? this.TtlSaveView : this.TtlNoNameSet;

+  }

+

+  onRowSelect(e: any) {

+    if (this.lazySelectedRow !== e.data) {

+      this.selectedRow = e.data;

+      this.viewName = e.data;

+    } else {

+      this.selectedRow = undefined;

+      this.viewName = '';

+    }

+    this.lazySelectedRow = this.selectedRow;

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/.gitkeep b/org.eclipse.mdm.application/src/main/webapp/src/assets/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/.gitkeep
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/environment.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/environment.png
new file mode 100644
index 0000000..497b321
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/environment.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/environment_file.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/environment_file.png
new file mode 100644
index 0000000..da793cc
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/environment_file.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/folder.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/folder.png
new file mode 100644
index 0000000..c5df8d8
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/folder.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/meaquantity.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/meaquantity.png
new file mode 100644
index 0000000..6313685
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/meaquantity.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/mearesult.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/mearesult.png
new file mode 100644
index 0000000..c47af9f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/mearesult.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/mearesult_3d.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/mearesult_3d.png
new file mode 100644
index 0000000..38c977a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/mearesult_3d.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/parameter.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/parameter.png
new file mode 100644
index 0000000..37d5b49
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/parameter.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/parameterset.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/parameterset.png
new file mode 100644
index 0000000..793f9d3
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/parameterset.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/project.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/project.png
new file mode 100644
index 0000000..ac0cf49
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/project.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/structurelevel.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/structurelevel.png
new file mode 100644
index 0000000..7f91ea6
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/structurelevel.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/submatrix.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/submatrix.png
new file mode 100644
index 0000000..66e6f56
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/submatrix.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/test.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/test.png
new file mode 100644
index 0000000..8ad659e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/test.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testequipment.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testequipment.png
new file mode 100644
index 0000000..dc0f27c
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testequipment.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testequipmentpart.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testequipmentpart.png
new file mode 100644
index 0000000..1b19176
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testequipmentpart.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testsequence.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testsequence.png
new file mode 100644
index 0000000..75f2c54
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testsequence.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testsequencepart.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testsequencepart.png
new file mode 100644
index 0000000..3de9cc2
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/testsequencepart.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/teststep.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/teststep.png
new file mode 100644
index 0000000..4b410e0
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/teststep.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/unitundertest.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/unitundertest.png
new file mode 100644
index 0000000..c0bf24e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/unitundertest.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/img/unitundertestpart.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/unitundertestpart.png
new file mode 100644
index 0000000..4b162d8
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/img/unitundertestpart.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/environments/environment.prod.ts b/org.eclipse.mdm.application/src/main/webapp/src/environments/environment.prod.ts
new file mode 100644
index 0000000..5d91006
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/environments/environment.prod.ts
@@ -0,0 +1,14 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+export const environment = {
+  production: true
+};
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/environments/environment.ts b/org.eclipse.mdm.application/src/main/webapp/src/environments/environment.ts
new file mode 100644
index 0000000..339ddaf
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/environments/environment.ts
@@ -0,0 +1,20 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+// The file contents for the current environment will overwrite these during build.
+// The build system defaults to the dev environment which uses `environment.ts`, but if you do
+// `ng build --env=prod` then `environment.prod.ts` will be used instead.
+// The list of which env maps to which file can be found in `angular-cli.json`.
+
+export const environment = {
+  production: false
+};
diff --git a/org.eclipse.mdm.application/src/main/webapp/error.jsp b/org.eclipse.mdm.application/src/main/webapp/src/error.jsp
similarity index 100%
rename from org.eclipse.mdm.application/src/main/webapp/error.jsp
rename to org.eclipse.mdm.application/src/main/webapp/src/error.jsp
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/favicon.ico b/org.eclipse.mdm.application/src/main/webapp/src/favicon.ico
new file mode 100644
index 0000000..8081c7c
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/favicon.ico
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/index.html b/org.eclipse.mdm.application/src/main/webapp/src/index.html
new file mode 100644
index 0000000..c854dc5
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/index.html
@@ -0,0 +1,27 @@
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
+
+<!DOCTYPE html>
+<html>
+  <head>
+    <base href="/" />
+    <title>openMDM5 Web</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <meta charset="utf-8">
+    <link rel="shortcut icon" href="/favicon.ico" />
+  </head>
+  <body>
+    <app-root>Loading...</app-root>
+  </body>
+</html>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/login.css b/org.eclipse.mdm.application/src/main/webapp/src/login.css
new file mode 100644
index 0000000..bd0540c
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/login.css
@@ -0,0 +1,59 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+body {
+  padding-top: 40px;
+  padding-bottom: 40px;
+  background-color: #eee;
+}
+
+.form-signin {
+  max-width: 330px;
+  padding: 15px;
+  margin: 0 auto;
+}
+
+.form-signin .form-signin-heading, .form-signin .checkbox {
+  margin-bottom: 10px;
+}
+
+.form-signin .checkbox {
+  font-weight: normal;
+}
+
+.form-signin .form-control {
+  position: relative;
+  height: auto;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  padding: 10px;
+  font-size: 16px;
+}
+
+.form-signin .form-control:focus {
+  z-index: 2;
+}
+
+.form-signin input[type="email"] {
+  margin-bottom: -1px;
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+.form-signin input[type="password"] {
+  margin-bottom: 10px;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/login.jsp b/org.eclipse.mdm.application/src/main/webapp/src/login.jsp
new file mode 100644
index 0000000..541fe2d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/login.jsp
@@ -0,0 +1,52 @@
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta name="description" content="">
+<meta name="author" content="">
+
+<title>MDM Web Login</title>
+
+<link rel='stylesheet' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'>
+<link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/login.css">
+
+
+</head>
+
+<body>
+	<div class="bform">
+		<div class="bform2">
+			<div class="container">
+
+				<form class="form-signin" method=post action="j_security_check">
+					<h2 class="form-signin-heading">MDM Web Login</h2>
+					<input type="text" class="form-control" name="j_username"
+						placeholder="Username" required autofocus> <input
+						type="password" class="form-control" name="j_password"
+						placeholder="Password" required>
+
+					<button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
+				</form>
+
+			</div>
+		</div>
+	</div>
+
+</body>
+</html>
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/main.ts b/org.eclipse.mdm.application/src/main/webapp/src/main.ts
new file mode 100644
index 0000000..43f228e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/main.ts
@@ -0,0 +1,24 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { enableProdMode } from '@angular/core';
+import { environment } from './environments/environment';
+import { AppModule } from './app/app.module';
+
+if (environment.production) {
+  enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/polyfills.ts b/org.eclipse.mdm.application/src/main/webapp/src/polyfills.ts
new file mode 100644
index 0000000..562984a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/polyfills.ts
@@ -0,0 +1,54 @@
+/*******************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Dennis Schroeder - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*******************************************************************************/
+
+// This file includes polyfills needed by Angular and is loaded before the app.
+// You can add your own extra polyfills to this file.
+import 'reflect-metadata';
+
+import 'core-js/es6/symbol';
+import 'core-js/es6/object';
+import 'core-js/es6/function';
+import 'core-js/es6/parse-int';
+import 'core-js/es6/parse-float';
+import 'core-js/es6/number';
+import 'core-js/es6/math';
+import 'core-js/es6/string';
+import 'core-js/es6/date';
+import 'core-js/es6/array';
+import 'core-js/es6/regexp';
+import 'core-js/es6/map';
+import 'core-js/es6/set';
+import 'core-js/es6/reflect';
+
+import 'core-js/es7/reflect';
+import 'zone.js/dist/zone';
+import 'rxjs/Rx';
+// If you need to support the browsers/features below, uncomment the import
+// and run `npm install import-name-here';
+// Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+
+// Needed for: IE9
+// import 'classlist.js';
+
+// Animations
+// Needed for: All but Chrome and Firefox, Not supported in IE9
+// import 'web-animations-js';
+
+// Date, currency, decimal and percent pipes
+// Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
+// import 'intl';
+
+// NgClass on SVG elements
+// Needed for: IE10, IE11
+// import 'classlist.js';
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/styles.css b/org.eclipse.mdm.application/src/main/webapp/src/styles.css
new file mode 100644
index 0000000..c780042
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/styles.css
@@ -0,0 +1,104 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+/* You can add global styles to this file, and also import other style files */
+
+body {
+  margin: 3em 1em 0 1em;
+}
+
+.navbar {
+  min-height: 2em !important;
+  margin-bottom: 1em;
+}
+
+.navbar-nav > li > a, .navbar-brand {
+    padding-top: 4px !important;
+    padding-bottom: 0 !important;
+    height: 2em;
+    font-size: 1em;
+}
+
+.table > thead > tr > th, .table > tbody > tr > td {
+  padding: 2px 4px;
+}
+
+body .mdmtree .ui-tree {
+  display: inline-block;
+  width: 100%;
+  height: 100%;
+}
+body .mdmtree .ui-treenode {
+  padding: 0;
+}
+body .mdmtree .ui-treenode .ui-treenode-content {
+  height: 1.5em;
+}
+body .mdmtree .ui-treenode .ui-treenode-content .ui-treenode-label {
+  padding-left: 0;
+}
+body .mdmtree .ui-treenode .ui-treenode-content .ui-treenode-icon {
+  margin: .2em 0; width: 15px;
+}
+body .mdmtree .ui-treenode .ui-treenode-content .ui-tree-toggler {
+  width: 1em;
+}
+
+.icon {
+  background-repeat: no-repeat;
+  background-position: left center;
+  padding-left: 18px;
+  height: 1em;
+  margin: 0;
+}
+.environment {
+  background-image: url("assets/img/environment.png");
+}
+.project {
+  background-image: url("assets/img/project.png");
+}
+.pool {
+  background-image: url("assets/img/structurelevel.png");
+}
+.test {
+  background-image: url("assets/img/test.png");
+}
+.teststep {
+  background-image: url("assets/img/teststep.png");
+}
+.measurement {
+  background-image: url("assets/img/mearesult.png");
+}
+.channelgroup {
+  background-image: url("assets/img/submatrix.png");
+}
+.channel {
+  background-image: url("assets/img/meaquantity.png");
+}
+
+.ui-datatable .ui-datatable-data > tr > td {
+  padding: .25em .5em !important;
+}
+
+.ui-datatable .ui-datatable-thead > tr > th {
+  padding: .50em .5em !important;
+}
+
+.greyed * {
+  color: #ccc !important;
+  border-color: #e3e3e3 !important;
+}
+
+div .thinheader {
+  margin-top: -5px;
+  margin-bottom: -5px;
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/test.ts b/org.eclipse.mdm.application/src/main/webapp/src/test.ts
new file mode 100644
index 0000000..99d902d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/test.ts
@@ -0,0 +1,45 @@
+/*******************************************************************************
+*  Copyright (c) 2017 Peak Solution GmbH                                       *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Matthias Koller, Johannes Stamm - initial implementation                    *
+*******************************************************************************/
+
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/long-stack-trace-zone';
+import 'zone.js/dist/proxy.js';
+import 'zone.js/dist/sync-test';
+import 'zone.js/dist/jasmine-patch';
+import 'zone.js/dist/async-test';
+import 'zone.js/dist/fake-async-test';
+import 'rxjs/Rx';
+import { getTestBed } from '@angular/core/testing';
+import {
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
+declare var __karma__: any;
+declare var require: any;
+
+// Prevent Karma from running prematurely.
+__karma__.loaded = function () {};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
+// Finally, start Karma to run the tests.
+__karma__.start();
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/tsconfig.json b/org.eclipse.mdm.application/src/main/webapp/src/tsconfig.json
new file mode 100644
index 0000000..1cf713a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/tsconfig.json
@@ -0,0 +1,18 @@
+{
+  "compilerOptions": {
+    "baseUrl": "",
+    "declaration": false,
+    "emitDecoratorMetadata": true,
+    "experimentalDecorators": true,
+    "lib": ["es6", "dom"],
+    "mapRoot": "./",
+    "module": "es6",
+    "moduleResolution": "node",
+    "outDir": "../dist/out-tsc",
+    "sourceMap": true,
+    "target": "es5",
+    "typeRoots": [
+      "../node_modules/@types"
+    ]
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/systemjs.config.js b/org.eclipse.mdm.application/src/main/webapp/systemjs.config.js
deleted file mode 100644
index 3bb9da1..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/systemjs.config.js
+++ /dev/null
@@ -1,42 +0,0 @@
-(function(global) {
-
-  // map tells the System loader where to look for things
-  var map = {
-    'app':                        'app', // 'dist',
-    'rxjs':                       'node_modules/rxjs',
-    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
-    '@angular':                   'node_modules/@angular',
-    'moment':                     'node_modules/moment/moment.js'
-  };
-
-  // packages tells the System loader how to load when no filename and/or no extension
-  var packages = {
-    'app':                        { main: 'main.js',  defaultExtension: 'js' },
-    'rxjs':                       { defaultExtension: 'js' },
-    'angular2-in-memory-web-api': { defaultExtension: 'js' },
-  };
-
-  var ngPackageNames = [
-    'common',
-    'compiler',
-    'core',
-    'http',
-    'platform-browser',
-    'platform-browser-dynamic',
-    'router',
-    'router-deprecated',
-    'upgrade',
-  ];
-  // Add package entries for angular packages
-  ngPackageNames.forEach(function(pkgName) {
-    packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
-  });
-
-  var config = {
-    map: map,
-    packages: packages
-  }
-
-  System.config(config);
-
-})(this);
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/basket/mdm-basket.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/basket/mdm-basket.component.html
deleted file mode 100644
index 961f262..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/basket/mdm-basket.component.html
+++ /dev/null
@@ -1,18 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<div>
-  <ul class="list-group">
-    <li *ngFor="let node of _basketService.Nodes" class="list-group-item" (click)="selectNode(node)" [ngClass]="isActive(node)">
-      {{node.name}}
-      <span class="glyphicon glyphicon-remove remove" (click)="removeNode(node)"></span>
-    </li>
-  </ul>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/details/mdm-detail-descriptive-data.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/details/mdm-detail-descriptive-data.component.html
deleted file mode 100644
index fee306e..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/details/mdm-detail-descriptive-data.component.html
+++ /dev/null
@@ -1,107 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<div class="row">
-  <div *ngIf="!contexts">
-    <div class="alert alert-info">
-      <strong>{{status}}</strong>
-    </div>
-  </div>
-  <tabset *ngIf="contexts">
-    <tab heading={{uut}}>
-      <accordion>
-        <accordion-group *ngFor="let template of contexts['UNITUNDERTEST']" heading={{template.name}}>
-          <table class="table table-hover">
-            <thead>
-              <tr>
-                <th>Name</th>
-                <th>Beauftragt</th>
-                <th>Gemessen</th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr *ngFor="let attr of template.attributes" [ngClass]="diff(attr.value[0], attr.value[1])">
-                  <td>{{attr.name}}</td>
-                  <td>{{attr.value[0]}}</td>
-                  <td>{{attr.value[1]}}</td>
-              </tr>
-            </tbody>
-          </table>
-        </accordion-group>
-      </accordion>
-    </tab>
-    <tab heading={{te}}>
-      <accordion>
-        <accordion-group *ngFor="let template of contexts['TESTSEQUENCE']" heading={{template.name}}>
-          <table class="table table-hover">
-            <thead>
-              <tr>
-                <th>Name</th>
-                <th>Beauftragt</th>
-                <th>Gemessen</th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr *ngFor="let attr of template.attributes" [ngClass]="diff(attr.value[0], attr.value[1])">
-                  <td>{{attr.name}}</td>
-                  <td>{{attr.value[0]}}</td>
-                  <td>{{attr.value[1]}}</td>
-              </tr>
-            </tbody>
-          </table>
-        </accordion-group>
-      </accordion>
-    </tab>
-    <tab heading={{ts}}>
-      <accordion>
-        <accordion-group *ngFor="let template of contexts['TESTEQUIPMENT']" heading={{template.name}}>
-          <table class="table table-hover">
-            <thead>
-              <tr>
-                <th>Name</th>
-                <th>Beauftragt</th>
-                <th>Gemessen</th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr *ngFor="let attr of template.attributes" [ngClass]="diff(attr.value[0], attr.value[1])">
-                  <td>{{attr.name}}</td>
-                  <td>{{attr.value[0]}}</td>
-                  <td>{{attr.value[1]}}</td>
-              </tr>
-            </tbody>
-          </table>
-        </accordion-group>
-      </accordion>
-    </tab>
-    <tab heading={{s}}>
-      <accordion>
-        <accordion-group *ngFor="let node of sensors" heading={{node.name}}>
-          <table class="table table-hover">
-            <thead>
-              <tr>
-                <th>Name</th>
-                <th>Beauftragt</th>
-                <th>Gemessen</th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr *ngFor="let attr of node.attributes" [ngClass]="diff(attr.value[0], attr.value[1])">
-                  <td>{{attr.name[0]}}</td>
-                  <td>{{attr.value[0]}}</td>
-                  <td>{{attr.value[1]}}</td>
-              </tr>
-            </tbody>
-          </table>
-        </accordion-group>
-      </accordion>
-    </tab>
-  </tabset>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/details/mdm-detail-view.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/details/mdm-detail-view.component.html
deleted file mode 100644
index 06ec1bf..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/details/mdm-detail-view.component.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<div class="btn-group" role="group">
-  <button type="button" class="btn btn-default" (click)="add2Basket()" [disabled]="isShopable()">In den Warenkorb</button>
-  <button type="button" class="btn btn-default" (click)="lgModal.show()" [disabled]="isReleasable()">Freigabeanfrage</button>
-</div>
-<table class="table table-hover">
-  <thead>
-    <tr>
-      <th>Attribut</th>
-      <th>Wert</th>
-      <th>Einheit</th>
-    </tr>
-  </thead>
-  <tbody *ngIf="selectedNode">
-    <tr *ngFor="let attr of selectedNode.attributes">
-      <td>{{getTrans(selectedNode.type, attr.name)}}</td>
-      <td>{{attr.value}}</td>
-      <td>{{attr.unit}}</td>
-    </tr>
-  </tbody>
-</table>
-
-<div bsModal #lgModal="bs-modal" class="modal fade" tabindex="-1" role="dialog">
-  <div class="modal-dialog modal-lg">
-    <div class="modal-content">
-      <div class="modal-header">
-        <button type="button" class="close" (click)="lgModal.hide()">
-          <span aria-hidden="true">&times;</span>
-        </button>
-        <h4 class="modal-title">Freigabe</h4>
-      </div>
-      <div class="modal-body">
-        <mdm-filerelease-create [node]=selectedNode (onSubmit)="lgModal.hide()"></mdm-filerelease-create>
-      </div>
-    </div>
-  </div>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/details/mdm-detail.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/details/mdm-detail.component.html
deleted file mode 100644
index 6d51fe1..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/details/mdm-detail.component.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<nav class="navbar navbar-default">
-  <div class="container-fluid">
-  <div class="navbar-header">
-    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-mdm-detail-navbar" aria-expaned="false">
-      <span class="icon-bar"></span>
-      <span class="icon-bar"></span>
-      <span class="icon-bar"></span>
-    </button>
-    <a class="navbar-brand">{{brand}}</a>
-  </div>
-  <div class="collapse navbar-collapse" id="bs-mdm-detail-navbar">
-    <ul class="nav navbar-nav">
-      <li [ngClass]="isActive('DetailView')"><a (click)="activate('DetailView')" style="cursor:pointer;"> Attribute</a></li>
-      <li [ngClass]="isActive('DescriptiveData')"><a (click)="activate('DescriptiveData')" style="cursor:pointer;"> Auftrags-/Messkontext</a></li>
-      <li [ngClass]="isActive('Search')"><a (click)="activate('Search')" style="cursor:pointer;"> MDM Suche</a></li>
-      <li [ngClass]="isActive('SearchFT')"><a (click)="activate('SearchFT')" style="cursor:pointer;"> Volltextsuche</a></li>
-      <li [ngClass]="isActive('Release')"><a (click)="activate('Release')" style="cursor:pointer;"> Freigabeübersicht</a></li>
-    </ul>
-  </div>
-</div>
-</nav>
-<div class="container-fluid" *ngIf="_comp==='DetailView'">
-  <mdm-detail-view [selectedNode]=selectedNode></mdm-detail-view>
-</div>
-<div class="container-fluid" *ngIf="_comp==='DescriptiveData'">
-  <mdm-detail-context [selectedNode]=selectedNode></mdm-detail-context>
-</div>
-<div class="container-fluid" *ngIf="_comp==='Search'">
-  <mdm-search></mdm-search>
-</div>
-<div class="container-fluid" *ngIf="_comp==='SearchFT'">
-  <mdm-full-text-search></mdm-full-text-search>
-</div>
-<div class="container-fluid" *ngIf="_comp==='Release'">
-  <mdm-filerelease></mdm-filerelease>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/filerelease/mdm-filerelease-create.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/filerelease/mdm-filerelease-create.component.html
deleted file mode 100644
index 0c51091..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/filerelease/mdm-filerelease-create.component.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<form (ngSubmit)="createRelease()" #request="ngForm">
-  <div class="form-group">
-
-    <div [ngClass]="validity.valid ? 'has-success' : 'has-error'">
-      <label class="control-label col-sm-2" for="validity">Gültigkeit(Tage):</label>
-      <div class="col-sm-10">
-        <select id="validity" [(ngModel)]="release.validity" ngControl="validity" #validity="ngForm" class="form-control" required>
-          <option *ngFor="let opt of expire" [ngValue]="opt">{{opt}}</option>
-        </select>
-      </div>
-    </div>
-
-    <div [ngClass]="format.valid ? 'has-success' : 'has-error'">
-      <label class="control-label col-sm-2" for="format">Format:</label>
-      <div class="col-sm-10">
-        <select id="format" [(ngModel)]="release.format" ngControl="format" #format="ngForm" class="form-control" required>
-          <option *ngFor="let opt of options" [ngValue]="opt">{{getFormat(opt)}}</option>
-        </select>
-      </div>
-    </div>
-
-    <div [ngClass]="orderMessage.valid ? 'has-success' : 'has-error'">
-      <label class="control-label col-sm-2" for="orderMessage">Begründung:</label>
-      <div class="col-sm-10">
-        <textarea id="orderMessage" [(ngModel)]="release.orderMessage" ngControl="orderMessage" #orderMessage="ngForm" cols="35" rows="4" class="form-control" required></textarea>
-      </div>
-    </div>
-
-  </div>
-  <div class="btn-group">
-    <button type="submit" class="btn btn-default" [disabled]=!request.form.valid>Senden</button>
-  </div>
-</form>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/filerelease/mdm-filerelease-display.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/filerelease/mdm-filerelease-display.component.html
deleted file mode 100644
index 97a92ce..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/filerelease/mdm-filerelease-display.component.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<table class="table table-bordered">
-  <tbody>
-    <tr>
-      <td>Versuchsname:</td><td>{{release.name}}</td>
-    </tr>
-    <tr>
-      <td>Fehlerbericht:</td><td>{{release.errorMessage}}</td>
-    </tr>
-    <tr>
-      <td>freigegeben bis:</td><td>{{getDate(release.expire)}}</td>
-    </tr>
-    <tr>
-      <td>Format:</td><td>{{getFormat(release.format)}}</td>
-    </tr>
-    <tr>
-      <td>Begründung für Freigabe:</td><td>{{release.orderMessage}}</td>
-    </tr>
-    <tr>
-      <td>Begründung für Ablehnung:</td><td>{{release.rejectMessage}}</td>
-    </tr>
-    <tr>
-      <td>Antragsteller:</td><td>{{release.sender}}</td>
-    </tr>
-    <tr>
-      <td>Messungsverantwortlicher:</td><td>{{release.receiver}}</td>
-    </tr>
-    <tr>
-      <td>Status:</td><td>{{getState(release.state)}}</td>
-    </tr>
-    <tr>
-      <td>Freigabedauer:</td><td>{{release.validity}} Tage</td>
-    </tr>
-  </tbody>
-</table>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/mdm.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/mdm.component.html
deleted file mode 100644
index e0d2cd6..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/mdm.component.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<nav class="navbar navbar-default">
-  <div class="container-fluid">
-    <div class="navbar-header">
-      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-mdm-navbar" aria-expaned="false">
-        <span class="icon-bar"></span>
-        <span class="icon-bar"></span>
-        <span class="icon-bar"></span>
-      </button>
-      <a class="navbar-brand">{{brand}}</a>
-    </div>
-    <div class="collapse navbar-collapse" id="bs-mdm-navbar">
-      <ul class="nav navbar-nav">
-        <li [ngClass]="isActive('MDMMenu')"><a [routerLink]="['/mdmmenu']" (click)="activate('MDMMenu')"> Navigator</a></li>
-      </ul>
-      <ul class="nav navbar-nav navbar-right">
-        <li><a href="mdm/logout"><span class="glyphicon glyphicon-log-in"></span> Logout</a></li>
-      </ul>
-    </div>
-  </div>
-</nav>
-<router-outlet></router-outlet>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/navigator/mdm-menu.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/navigator/mdm-menu.component.html
deleted file mode 100644
index a2aac54..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/navigator/mdm-menu.component.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<div class="container-fluid">
-  <div class="row">
-    <div class="col-sm-3">
-      <accordion [closeOthers]="closeOther">
-        <accordion-group heading={{navigator}} [isOpen]="true">
-            <mdm-navigator (selectingNode)="updateSelectedNode($event)" [activeNode]=activeNode (onActive)="updateActiveNode($event)"></mdm-navigator>
-        </accordion-group>
-        <accordion-group heading={{basket}} [isOpen]="true">
-          <mdm-basket (onSelect)="updateSelectedNode($event)" [activeNode]=activeNode (onActive)="updateActiveNode($event)"></mdm-basket>
-        </accordion-group>
-      </accordion>
-    </div>
-    <div class="col-sm-9">
-      <mdm-detail [selectedNode]=selectedNode></mdm-detail>
-      <router-outlet></router-outlet>
-    </div>
-  </div>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/navigator/mdm-navigator.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/navigator/mdm-navigator.component.html
deleted file mode 100644
index 66c56b6..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/navigator/mdm-navigator.component.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<ul class="list-group" style="max-height:70vh;overflow-y:auto;">
-  <div *ngFor="let node of nodes">
-    <li class="list-group-item" [ngClass]="isActive(node)">
-      <span style="cursor: pointer;" [style.margin-left.px]="getMargin()" [ngClass]="isOpen(node)" (click)="onOpenNode(node)"></span>
-      <a style="color:black; cursor: pointer;" (click)="onSelectNode(node)">{{node.name}}</a>
-    </li>
-    <mdm-node-provider *ngIf="node.active" [rootNode]="openNode" [indent]="getMargin()" [activeNode]="activeNode" (selectingNode)="updateSelectedNode($event)" (onActive)="updateActiveNode($event)">Loading...</mdm-node-provider>
-  </div>
-</ul>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/navigator/mdm-node-provider.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/navigator/mdm-node-provider.component.html
deleted file mode 100644
index 4bdf544..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/navigator/mdm-node-provider.component.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schreoder - initial implementation
-  ******************************************************************************* -->
-<div *ngFor="let node of nodes">
-  <li class="list-group-item" [ngClass]="isActive(node)">
-    <span style="cursor: pointer;" [style.margin-left.px]="getMargin()" [ngClass]="isOpen(node)" (click)="onOpenNode(node)"></span>
-    <a style="color:black; cursor: pointer;" (click)="onSelectNode(node)">{{node.name}}</a>
-  </li>
-  <mdm-node-provider *ngIf="node.active" [rootNode]="openNode" [indent]="getMargin()" [activeNode]="activeNode" (selectingNode)="updateSelectedNode($event)" (onActive)="updateActiveNode($event)">Loading...</mdm-node-provider>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/search/dynamic-form-search.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/search/dynamic-form-search.component.html
deleted file mode 100644
index cb0d800..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/search/dynamic-form-search.component.html
+++ /dev/null
@@ -1,27 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<div [ngFormModel]="form">
-  <div [ngSwitch]="search.controlType" class="form-group">
-    <label for="search.key" class="col-sm-1 control-label">{{getTrans(search.label)}}:</label>
-    <div class="col-sm-11 input-group">
-      <input *ngSwitchWhen="'textbox'" [ngControl]="search.key"
-              [id]="search.key" [type]="search.type"
-              class="form-control" placeholder="{{getTrans(search.label)}}">
-      <select [id]="search.key" *ngSwitchWhen="'dropdown'" [ngControl]="search.key">
-        <option *ngFor="let opt of search.options" [value]="opt.key">{{opt.value}}</option>
-      </select>
-      <span class="input-group-btn">
-        <button type="button" class="btn btn-default" (click)="removeItem()"><span class="glyphicon glyphicon-remove"></span></button>
-      </span>
-    </div>
-  </div>
-  <div class="errorMessage" *ngIf="!isValid">{{search.label}} is required</div>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/search/dynamic-form.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/search/dynamic-form.component.html
deleted file mode 100644
index 0573697..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/search/dynamic-form.component.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<div>
-  <form (ngSubmit)="onSubmit()" [ngFormModel]="form">
-    <div *ngFor="let group of groups" class="form-row">
-      <accordion>
-         <accordion-group *ngIf="hasActiveItems(group.name)" heading="{{getTrans(group.name)}}" [isOpen]=true>
-           <div *ngFor="let search of group.items">
-             <df-search *ngIf="search.active" [search]="search" [form]="form"></df-search>
-           </div>
-         </accordion-group>
-      </accordion>
-    </div>
-    <div class="form-row">
-      <button class="btn btn-default" type="submit" [disabled]="!form.valid">Suchen</button>
-    </div>
-  </form>
-  <h4>Ergebnisse:</h4>
-  <div *ngIf="nodes">
-    <ul class="list-group">
-      <li *ngFor="let node of nodes" class="list-group-item">
-        <span (click)="add2Basket(node)" class="glyphicon glyphicon-shopping-cart" style="cursor: pointer;"></span> {{node.name}}
-      </li>
-    </ul>
-  </div>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/search/mdm-full-text-search.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/search/mdm-full-text-search.component.html
deleted file mode 100644
index f1524e8..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/search/mdm-full-text-search.component.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-<div class="container-fluid">
-  <div class="row">
-    <div class="form-group">
-      <form>
-        <label *ngFor="let env of envs" class="radio-inline"><input type="checkbox" name="env" (click)="selectEnv(env.sourceName)" checked>{{env.name}}</label>
-      </form>
-    </div>
-  </div>
-</div>
-<div class="container-fluid">
-  <div class="input-group">
-    <input class="form-control" #query
-    (keyup.enter)="onSubmit(query.value)">
-    <span class="input-group-btn">
-      <button type="button" class="btn btn-default" (click)="onSubmit(query.value)">Go!</button>
-    </span>
-  </div>
-</div>
-<div class="container-fluid">
-<h4>Ergebnisse:</h4>
-  <div *ngIf="nodes">
-    <ul class="list-group">
-      <li *ngFor="let node of nodes" class="list-group-item">
-        <span (click)="add2Basket(node)" class="glyphicon glyphicon-shopping-cart" style="cursor: pointer;"></span> {{node.name}} 
-      </li>
-    </ul>
-  </div>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/templates/search/mdm-search.component.html b/org.eclipse.mdm.application/src/main/webapp/templates/search/mdm-search.component.html
deleted file mode 100644
index ff2e37c..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/templates/search/mdm-search.component.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<!-- *******************************************************************************
-  * Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-  * All rights reserved. This program and the accompanying materials
-  * are made available under the terms of the Eclipse Public License v1.0
-  * which accompanies this distribution, and is available at
-  * http://www.eclipse.org/legal/epl-v10.html
-  *
-  * Contributors:
-  * Dennis Schroeder - initial implementation
-  ******************************************************************************* -->
-  <div class="container-fluid">
-    <div class="row">
-      <div class="col-lg-12">
-        <div class="form-group">
-          <form>
-            <label *ngFor="let env of envs" class="radio-inline"><input type="checkbox" name="env" (click)="selectEnv(env.sourceName)" checked>{{env.name}}</label>
-          </form>
-        </div>
-      </div>
-    </div>
-  </div>
-
-  <div class="container-fluid">
-    <div class="input-group">
-      <input [(ngModel)]="selected"
-             [typeahead]="ungrouped"
-             (typeaheadOnSelect)="typeaheadOnSelect($event)"
-             [typeaheadOptionField]="'labelt'"
-             class="form-control">
-      <span class="input-group-btn">
-        <div class="btn-group" style="z-index:4;" dropdown>
-          <button id="single-button" type="button" class="btn btn-default" dropdownToggle [disabled]="disabled">
-            Suchen nach {{type.label}} <span class="caret"></span>
-          </button>
-          <ul class="dropdown-menu" role="menu" aria-labelledby="single-button">
-            <li *ngFor="let opt of definitions.options" role="menuitem"><a class="dropdown-item" (click)="selectDef(opt)">{{opt.label}}</a></li>
-          </ul>
-        </div>
-        <button type="button" class="btn btn-default btn" (click)="lgModal.show()">alle Anzeigen</button>
-      </span>
-    </div>
-  </div>
-
-  <div class="container-fluid">
-    <div class="form-group">
-      <dynamic-form [groups]="groups" [env]="selectedEnv" [type]="type.value"></dynamic-form>
-    </div>
-  </div>
-
-<div bsModal #lgModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="SelectSearchComponents" aria-hidden="true">
-  <div class="modal-dialog modal-lg">
-    <div class="modal-content">
-      <div class="modal-header">
-        <button type="button" class="close" (click)="lgModal.hide()" aria-label="Close">
-          <span aria-hidden="true">&times;</span>
-        </button>
-        <h4 class="modal-title">Suchbare Komponenten</h4>
-      </div>
-      <div class="modal-body">
-        <accordion>
-           <accordion-group *ngFor="let group of groups" heading="{{getTrans(group.name)}}">
-             <ul class="list-group">
-               <li class="list-group-item" *ngFor="let item of group.items" (click)="selectItem(item)" [ngClass]="isActive(item)">
-                 {{getTrans(item.label)}}
-               </li>
-             </ul>
-           </accordion-group>
-        </accordion>
-      </div>
-    </div>
-  </div>
-</div>
diff --git a/org.eclipse.mdm.application/src/main/webapp/tsconfig.json b/org.eclipse.mdm.application/src/main/webapp/tsconfig.json
deleted file mode 100644
index e9772ed..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/tsconfig.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "compilerOptions": {
-    "target": "es5",
-    "module": "commonjs",
-    "moduleResolution": "node",
-    "sourceMap": true,
-    "emitDecoratorMetadata": true,
-    "experimentalDecorators": true,
-    "removeComments": false,
-    "noImplicitAny": false,
-    "suppressImplicitAnyIndexErrors": true
-  },
-  "exclude": [
-    "node_modules",
-    "typings/main",
-    "typings/main.d.ts"
-  ]
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/tslint.json b/org.eclipse.mdm.application/src/main/webapp/tslint.json
new file mode 100644
index 0000000..4fb95db
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/tslint.json
@@ -0,0 +1,116 @@
+{
+  "rulesDirectory": [
+    "node_modules/codelyzer"
+  ],
+  "rules": {
+    "callable-types": true,
+    "class-name": true,
+    "comment-format": [
+      true,
+      "check-space"
+    ],
+    "curly": true,
+    "eofline": true,
+    "forin": true,
+    "import-blacklist": [true, "rxjs"],
+    "import-spacing": true,
+    "indent": [
+      true,
+      2
+    ],
+    "interface-over-type-literal": true,
+    "label-position": true,
+    "max-line-length": [
+      true,
+      140
+    ],
+    "member-access": false,
+    "member-ordering": [
+      true,
+      "static-before-instance",
+      "variables-before-functions"
+    ],
+    "no-arg": true,
+    "no-bitwise": true,
+    "no-console": [
+      true,
+      "debug",
+      "info",
+      "time",
+      "timeEnd",
+      "trace"
+    ],
+    "no-construct": true,
+    "no-debugger": true,
+    "no-duplicate-variable": true,
+    "no-empty": false,
+    "no-empty-interface": true,
+    "no-eval": true,
+    "no-inferrable-types": true,
+    "no-shadowed-variable": true,
+    "no-string-literal": false,
+    "no-string-throw": true,
+    "no-switch-case-fall-through": true,
+    "no-trailing-whitespace": true,
+    "no-unused-expression": true,
+    "no-use-before-declare": true,
+    "no-var-keyword": true,
+    "object-literal-sort-keys": false,
+    "one-line": [
+      true,
+      "check-open-brace",
+      "check-catch",
+      "check-else",
+      "check-whitespace"
+    ],
+    "prefer-const": false,
+    "quotemark": [
+      true,
+      "single"
+    ],
+    "radix": true,
+    "semicolon": [
+      "always"
+    ],
+    "triple-equals": [
+      true,
+      "allow-null-check"
+    ],
+    "typedef-whitespace": [
+      true,
+      {
+        "call-signature": "nospace",
+        "index-signature": "nospace",
+        "parameter": "nospace",
+        "property-declaration": "nospace",
+        "variable-declaration": "nospace"
+      }
+    ],
+    "typeof-compare": true,
+    "unified-signatures": true,
+    "variable-name": false,
+    "whitespace": [
+      true,
+      "check-branch",
+      "check-decl",
+      "check-operator",
+      "check-separator",
+      "check-type"
+    ],
+
+    "directive-selector": [false, "attribute", "app", "camelCase"],
+    "component-selector": [false, "element", "app", "kebab-case"],
+    "use-input-property-decorator": true,
+    "use-output-property-decorator": true,
+    "use-host-property-decorator": true,
+    "no-input-rename": true,
+    "no-output-rename": true,
+    "use-life-cycle-interface": true,
+    "use-pipe-transform-interface": true,
+    "component-class-suffix": true,
+    "directive-class-suffix": true,
+    "no-access-missing-member": true,
+    "templates-use-public": true,
+    "invoke-injectable": true
+  }
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/typings.json b/org.eclipse.mdm.application/src/main/webapp/typings.json
deleted file mode 100644
index ab3d27d..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/typings.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "ambientDependencies": {
-    "core-js": "registry:dt/core-js#0.0.0+20160317120654",
-    "jasmine": "registry:dt/jasmine#2.2.0+20160412134438",
-    "node": "registry:dt/node#4.0.0+20160509154515"
-  }
-}
diff --git a/org.eclipse.mdm.application/src/main/webapp/webpack.config.js b/org.eclipse.mdm.application/src/main/webapp/webpack.config.js
deleted file mode 100644
index cda476b..0000000
--- a/org.eclipse.mdm.application/src/main/webapp/webpack.config.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('./config/webpack.prod.js');
diff --git a/org.eclipse.mdm.application/src/main/webconfig/glassfish-web.xml b/org.eclipse.mdm.application/src/main/webconfig/glassfish-web.xml
index a6741f2..6599d9d 100644
--- a/org.eclipse.mdm.application/src/main/webconfig/glassfish-web.xml
+++ b/org.eclipse.mdm.application/src/main/webconfig/glassfish-web.xml
@@ -1,18 +1,19 @@
-<!-- 
-*******************************************************************************
-* Copyright (c) 2016 Gigatronik Ingolstadt GmbH
-* All rights reserved. This program and the accompanying materials
-* are made available under the terms of the Eclipse Public License v1.0
-* which accompanies this distribution, and is available at
-* http://www.eclipse.org/legal/epl-v10.html
-*
-* Contributors:
-* Sebastian Dirsch - initial implementation
-*******************************************************************************/
---> 
-  
-<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD 
-GlassFish Application Server 3.1 Servlet 3.0//EN" 
+<!--****************************************************************************
+*  Original work: Copyright (c) 2016 Gigatronik Ingolstadt GmbH                *
+*  Modified work: Copyright (c) 2017 Peak Solution GmbH                        *
+*                                                                              *
+*  All rights reserved. This program and the accompanying materials            *
+*  are made available under the terms of the Eclipse Public License v1.0       *
+*  which accompanies this distribution, and is available at                    *
+*  http://www.eclipse.org/legal/epl-v10.html                                   *
+*                                                                              *
+*  Contributors:                                                               *
+*  Sebastian Dirsch - initial implementation                                   *
+*  Matthias Koller, Johannes Stamm - additional client functionality           *
+*****************************************************************************-->
+
+<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD
+GlassFish Application Server 3.1 Servlet 3.0//EN"
 "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
 <glassfish-web-app>
     <security-role-mapping>
@@ -20,4 +21,4 @@
         <group-name>MDM</group-name>
     </security-role-mapping>
 
-</glassfish-web-app>
\ No newline at end of file
+</glassfish-web-app>
diff --git a/org.eclipse.mdm.businessobjects/build.gradle b/org.eclipse.mdm.businessobjects/build.gradle
index 555817f..80fd57d 100644
--- a/org.eclipse.mdm.businessobjects/build.gradle
+++ b/org.eclipse.mdm.businessobjects/build.gradle
@@ -13,5 +13,8 @@
 version = '1.0.0'
 
 dependencies {
-	compile project(':org.eclipse.mdm.connector')	
+  compile project(':org.eclipse.mdm.connector')	
+  compile 'com.fasterxml.jackson.core:jackson-databind:2.8.6'
+
+  testCompile 'org.assertj:assertj-core:3.6.2'
 }
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolResource.java
new file mode 100644
index 0000000..741eaf0
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolResource.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+  * Copyright (c) 2017 Peak Solution GmbH
+  * All rights reserved. This program and the accompanying materials
+  * are made available under the terms of the Eclipse Public License v1.0
+  * which accompanies this distribution, and is available at
+  * http://www.eclipse.org/legal/epl-v10.html
+  *
+  * Contributors:
+  * Matthias Koller - initial implementation
+  *******************************************************************************/
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.Attribute;
+import org.eclipse.mdm.api.base.query.EntityType;
+import org.eclipse.mdm.api.dflt.model.Pool;
+import org.eclipse.mdm.businessobjects.entity.I18NResponse;
+import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.entity.SearchAttributeResponse;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link Pool} resource
+ * 
+ * @author Matthias Koller, Peak Solution GmbH
+ *
+ */
+@Path("/environments/{SOURCENAME}/pools")
+public class PoolResource {
+
+	private static final Logger LOG = LoggerFactory.getLogger(PoolResource.class);
+
+	@EJB
+	private PoolService poolService;
+
+	/**
+	 * delegates the request to the {@link PoolService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link Pool} result
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getPools(@PathParam("SOURCENAME") String sourceName, @QueryParam("filter") String filter) {
+		try {
+			List<Pool> pools = this.poolService.getPools(sourceName, filter);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Pool.class, pools), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error("Cannot load pools!", e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * delegates the request to the {@link PoolService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam("SOURCENAME") String sourceName) {
+		try {
+			List<SearchAttribute> searchAttributes = this.poolService.getSearchAttributes(sourceName);
+			return ServiceUtils.toResponse(new SearchAttributeResponse(searchAttributes), Status.OK);
+		} catch (RuntimeException e) {
+			LOG.error("Cannot load search attributes!", e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * delegates the request to the {@link PoolService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param poolId
+	 *            id of the {@link Pool}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{POOL_ID}")
+	public Response getPool(@PathParam("SOURCENAME") String sourceName, @PathParam("POOL_ID") long poolId) {
+		try {
+			Pool pool = this.poolService.getPool(sourceName, poolId);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Pool.class, pool), Status.OK);
+		} catch (RuntimeException e) {
+			LOG.error("Cannot load pools!", e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * delegates the request to the {@link PoolService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam("SOURCENAME") String sourceName) {
+
+		try {
+			Map<Attribute, String> localizedAttributeMap = this.poolService.localizeAttributes(sourceName);
+			Map<EntityType, String> localizedEntityTypeMap = this.poolService.localizeType(sourceName);
+			return ServiceUtils.toResponse(new I18NResponse(localizedEntityTypeMap, localizedAttributeMap), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error("Cannot load localizations!", e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolService.java
new file mode 100644
index 0000000..a92b27d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolService.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+  * Copyright (c) 2017 Peak Solution GmbH
+  * All rights reserved. This program and the accompanying materials
+  * are made available under the terms of the Eclipse Public License v1.0
+  * which accompanies this distribution, and is available at
+  * http://www.eclipse.org/legal/epl-v10.html
+  *
+  * Contributors:
+  * Matthias Koller - initial implementation
+  *******************************************************************************/
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.Attribute;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.base.query.EntityType;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.api.dflt.model.Pool;
+import org.eclipse.mdm.businessobjects.control.I18NActivity;
+import org.eclipse.mdm.businessobjects.control.MDMEntityAccessException;
+import org.eclipse.mdm.businessobjects.control.NavigationActivity;
+import org.eclipse.mdm.businessobjects.control.SearchActivity;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+
+/**
+ * PoolService Bean implementation with available {@link Pool} operations
+ * @author Matthias Koller, Peak Solution GmbH
+ *
+ */
+@Stateless
+public class PoolService {
+
+	@EJB
+	private ConnectorService connectorService;	
+	@EJB
+	private I18NActivity i18nActivity;
+	@EJB
+	private SearchActivity searchActivity;
+	@EJB
+	private NavigationActivity navigationActivity;
+	
+	/**
+	 * Default no-arg constructor for EJB
+	 */
+	public PoolService() {
+		// Default no-arg constructor for EJB
+	}
+	
+	/**
+	 * Contructor for unit testing
+	 * @param connectorService {@link ConnectorService} to use
+	 * @param searchActivity {@link SearchActivity} to use
+	 * @param navigationActivity {@link NavigationActivity} to use
+	 * @param i18nActivity {@link I18NActivity} to use
+	 */
+	PoolService(ConnectorService connectorService, SearchActivity searchActivity, NavigationActivity navigationActivity, I18NActivity i18nActivity) {
+		this.connectorService = connectorService;
+		this.searchActivity = searchActivity;
+		this.navigationActivity = navigationActivity;
+		this.i18nActivity = i18nActivity;
+	}
+	
+	/**
+	 * returns the matching {@link Pool}s using the given filter or all {@link Pool}s 
+	 * if no filter is available
+	 * 
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @param filter filter string to filter the {@link Pool} result
+	 * @return the found {@link Pool}s
+	 */
+	public List<Pool> getPools(String sourceName, String filter) {
+		
+		try {		
+			EntityManager em = this.connectorService.getEntityManagerByName(sourceName);
+			
+			if(filter == null || filter.trim().length() <= 0) {
+				return em.loadAll(Pool.class);
+			}		
+			
+			if(ServiceUtils.isParentFilter(em, filter, Pool.PARENT_TYPE_PROJECT)) {
+				long id = ServiceUtils.extactIdFromParentFilter(em, filter, Pool.PARENT_TYPE_PROJECT);
+				return this.navigationActivity.getPools(sourceName, id);
+			}
+			
+			return this.searchActivity.search(em, Pool.class, filter);
+		} catch(DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+	
+	/**
+	 * Returns the {@link SearchAttribute} for the entity type {@link Pool} in the given data source.
+	 * @param sourceName The name of the data source.
+	 * @return the found {@link SearchAttribute}s
+	 */
+	public List<SearchAttribute> getSearchAttributes(String sourceName) {
+		EntityManager em = this.connectorService.getEntityManagerByName(sourceName);
+		return this.searchActivity.listAvailableAttributes(em, Pool.class);
+	}
+	
+	/**
+	 * returns a {@link Pool} identified by the given id.
+	 * @param poolId id of the {@link Pool}
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @param testStepId id of the {@link Pool}
+	 * @return the matching {@link Pool}
+	 */
+	public Pool getPool(String sourceName, long poolId) {
+		try {		
+			EntityManager em = this.connectorService.getEntityManagerByName(sourceName);
+			return em.load(Pool.class, poolId);
+		} catch(DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+	
+	/**
+	 * returns localized {@link Pool} attributes
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Pool} attributes
+	 */
+	public Map<Attribute, String> localizeAttributes(String sourceName) {		
+		return this.i18nActivity.localizeAttributes(sourceName, Pool.class);
+	}	
+	
+	/**
+	 * returns the localized {@link Pool} type name
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Pool} type name
+	 */
+	public Map<EntityType, String> localizeType(String sourceName) {
+		return this.i18nActivity.localizeType(sourceName, Pool.class);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ProjectResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ProjectResource.java
new file mode 100644
index 0000000..5754c97
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ProjectResource.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+  * Copyright (c) 2017 Peak Solution GmbH
+  * All rights reserved. This program and the accompanying materials
+  * are made available under the terms of the Eclipse Public License v1.0
+  * which accompanies this distribution, and is available at
+  * http://www.eclipse.org/legal/epl-v10.html
+  *
+  * Contributors:
+  * Matthias Koller - initial implementation
+  *******************************************************************************/
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.Attribute;
+import org.eclipse.mdm.api.base.query.EntityType;
+import org.eclipse.mdm.api.dflt.model.Project;
+import org.eclipse.mdm.businessobjects.entity.I18NResponse;
+import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.entity.SearchAttributeResponse;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link Project} resource
+ * 
+ * @author Matthias Koller, Peak Solution GmbH
+ *
+ */
+@Path("/environments/{SOURCENAME}/projects")
+public class ProjectResource {
+
+	private static final Logger LOG = LoggerFactory.getLogger(ProjectResource.class);
+
+	@EJB
+	private ProjectService projectService;
+
+	/**
+	 * delegates the request to the {@link ProjectService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link Project} result
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getProjects(@PathParam("SOURCENAME") String sourceName, @QueryParam("filter") String filter) {
+		try {
+			List<Project> projects = this.projectService.getProjects(sourceName, filter);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Project.class, projects), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error("Cannot load Projects!", e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * delegates the request to the {@link ProjectService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam("SOURCENAME") String sourceName) {
+		try {
+			List<SearchAttribute> searchAttributes = this.projectService.getSearchAttributes(sourceName);
+			return ServiceUtils.toResponse(new SearchAttributeResponse(searchAttributes), Status.OK);
+		} catch (RuntimeException e) {
+			LOG.error("Cannot load search attributes", e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * delegates the request to the {@link ProjectService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param projectId
+	 *            id of the {@link Project}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{PROJECT_ID}")
+	public Response getProject(@PathParam("SOURCENAME") String sourceName, @PathParam("PROJECT_ID") long projectId) {
+		try {
+			Project project = this.projectService.getProject(sourceName, projectId);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Project.class, project), Status.OK);
+		} catch (RuntimeException e) {
+			LOG.error("Cannot load project!", e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * delegates the request to the {@link ProjectService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam("SOURCENAME") String sourceName) {
+
+		try {
+			Map<Attribute, String> localizedAttributeMap = this.projectService.localizeAttributes(sourceName);
+			Map<EntityType, String> localizedEntityTypeMap = this.projectService.localizeType(sourceName);
+			return ServiceUtils.toResponse(new I18NResponse(localizedEntityTypeMap, localizedAttributeMap), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error("Cannot load localizations!", e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ProjectService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ProjectService.java
new file mode 100644
index 0000000..a22a1fe
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ProjectService.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+  * Copyright (c) 2017 Peak Solution GmbH
+  * All rights reserved. This program and the accompanying materials
+  * are made available under the terms of the Eclipse Public License v1.0
+  * which accompanies this distribution, and is available at
+  * http://www.eclipse.org/legal/epl-v10.html
+  *
+  * Contributors:
+  * Matthias Koller - initial implementation
+  *******************************************************************************/
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.Attribute;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.base.query.EntityType;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.api.dflt.model.Project;
+import org.eclipse.mdm.businessobjects.control.I18NActivity;
+import org.eclipse.mdm.businessobjects.control.MDMEntityAccessException;
+import org.eclipse.mdm.businessobjects.control.SearchActivity;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+
+/**
+ * ProjectService Bean implementation with available {@link Project} operations
+ * @author Matthias Koller, Peak Solution GmbH
+ *
+ */
+@Stateless
+public class ProjectService {
+
+	@EJB
+	private ConnectorService connectorService;	
+	@EJB
+	private I18NActivity i18nActivity;
+	@EJB
+	private SearchActivity searchActivity;
+	
+	/**
+	 * Default no-arg constructor for EJB
+	 */
+	public ProjectService() {
+		// Default no-arg constructor for EJB
+	}
+	
+	/**
+	 * Contructor for unit testing
+	 * @param connectorService {@link ConnectorService} to use
+	 * @param searchActivity {@link SearchActivity} to use
+	 * @param i18nActivity {@link I18NActivity} to use
+	 */
+	ProjectService(ConnectorService connectorService, SearchActivity searchActivity, I18NActivity i18nActivity) {
+		this.connectorService = connectorService;
+		this.searchActivity = searchActivity;
+		this.i18nActivity = i18nActivity;
+	}
+
+	/**
+	 * returns the matching {@link Project}s using the given filter or all {@link Project}s 
+	 * if no filter is available
+	 * 
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @param filter filter string to filter the {@link Project} result
+	 * @return the found {@link Project}s
+	 */
+	public List<Project> getProjects(String sourceName, String filter) {
+		
+		try {		
+			EntityManager em = this.connectorService.getEntityManagerByName(sourceName);
+			
+			if(filter == null || filter.trim().length() <= 0) {
+				return em.loadAll(Project.class);
+			}		
+			
+			return this.searchActivity.search(em, Project.class, filter);
+		} catch(DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+	
+	/**
+	 * Returns the {@link SearchAttribute} for the entity type {@link Project} in the given data source.
+	 * @param sourceName The name of the data source.
+	 * @return the found {@link SearchAttribute}s
+	 */
+	public List<SearchAttribute> getSearchAttributes(String sourceName) {
+		EntityManager em = this.connectorService.getEntityManagerByName(sourceName);
+		return this.searchActivity.listAvailableAttributes(em, Project.class);
+	}
+	
+	/**
+	 * returns a {@link Project} identified by the given id.
+	 * @param projectId id of the {@link Project}
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @param testStepId id of the {@link Project}
+	 * @return the matching {@link Project}
+	 */
+	public Project getProject(String sourceName, long projectId) {
+		try {		
+			EntityManager em = this.connectorService.getEntityManagerByName(sourceName);
+			return em.load(Project.class, projectId);
+		} catch(DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+	
+	/**
+	 * returns localized {@link Project} attributes
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Project} attributes
+	 */
+	public Map<Attribute, String> localizeAttributes(String sourceName) {		
+		return this.i18nActivity.localizeAttributes(sourceName, Project.class);
+	}	
+	
+	/**
+	 * returns the localized {@link Project} type name
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Project} type name
+	 */
+	public Map<EntityType, String> localizeType(String sourceName) {
+		return this.i18nActivity.localizeType(sourceName, Project.class);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestService.java
index a7a4d7c..347b746 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestService.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestService.java
@@ -22,10 +22,13 @@
 import org.eclipse.mdm.api.base.query.DataAccessException;
 import org.eclipse.mdm.api.base.query.EntityType;
 import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.api.dflt.model.Pool;
 import org.eclipse.mdm.businessobjects.control.I18NActivity;
 import org.eclipse.mdm.businessobjects.control.MDMEntityAccessException;
+import org.eclipse.mdm.businessobjects.control.NavigationActivity;
 import org.eclipse.mdm.businessobjects.control.SearchActivity;
 import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
 import org.eclipse.mdm.connector.boundary.ConnectorService;
 
 /**
@@ -42,7 +45,8 @@
 	private I18NActivity i18nActivity;
 	@EJB
 	private SearchActivity searchActivity;
-	
+	@EJB
+	private NavigationActivity navigationActivity;
 	
 
 	/**
@@ -62,6 +66,11 @@
 				return em.loadAll(Test.class);
 			}		
 			
+			if(ServiceUtils.isParentFilter(em, filter, Pool.class)) {
+				long id = ServiceUtils.extactIdFromParentFilter(em, filter, Pool.class);
+				return this.navigationActivity.getTests(sourceName, id);
+			}
+	
 			return this.searchActivity.search(em, Test.class, filter);
 		} catch(DataAccessException e) {
 			throw new MDMEntityAccessException(e.getMessage(), e);
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/NavigationActivity.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/NavigationActivity.java
index 5fcd7ba..eb73101 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/NavigationActivity.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/NavigationActivity.java
@@ -26,6 +26,8 @@
 import org.eclipse.mdm.api.base.model.TestStep;
 import org.eclipse.mdm.api.base.query.DataAccessException;
 import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.api.dflt.model.Pool;
+import org.eclipse.mdm.api.dflt.model.Project;
 import org.eclipse.mdm.connector.boundary.ConnectorService;
 
 /**
@@ -58,8 +60,21 @@
 			throw new MDMEntityAccessException(e.getMessage(), e);
 		}
 	}
-
 	
+	/**
+	 * returns all MDM {@link Project} business objects of the connected MDM system identified by the given name
+	 * 
+	 * @param sourceName Name of the MDM system
+	 * @return MDM {@link Project} business objects
+	 */
+	public List<Project> getProjects(String sourceName) {
+		try {
+			EntityManager em = this.connectorService.getEntityManagerByName(sourceName);
+			return em.loadAll(Project.class);			
+		} catch(DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
 	
 	/**
 	 * returns all MDM {@link Test} business objects of the connected MDM system identified by the given name
@@ -76,6 +91,29 @@
 		}
 	}
 
+	/**
+	 * returns all MDM {@link Test} business object children for a MDM {@link Pool} 
+	 * identified by the given source name and {@link Pool} ID.
+	 * 
+	 * @param sourceName Name of the MDM system
+	 * @param poolID The {@code Pool} instance ID
+	 * @return MDM {@link Test} business objects
+	 */
+	public List<Test> getTests(String sourceName, Long poolID) {
+		return getChildren(sourceName, Pool.class, poolID, Test.class);
+	}
+	
+	/**
+	 * returns all MDM {@link Pool} business object children for a MDM {@link Project} 
+	 * identified by the given source name and {@link Project} ID.
+	 * 
+	 * @param sourceName Name of the MDM system
+	 * @param projectID The {@code Project} instance ID
+	 * @return MDM {@link Pool} business objects
+	 */
+	public List<Pool> getPools(String sourceName, Long projectID) {
+		return getChildren(sourceName, Project.class, projectID, Pool.class);
+	}
 	
 	/**
 	 * returns all MDM {@link TestStep} business object children for a MDM {@link Test} 
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchActivity.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchActivity.java
index a7b0f9c..98c529f 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchActivity.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchActivity.java
@@ -79,7 +79,7 @@
 		for (EntityType entityType : entityTypes) {
 			for (Attribute attr : entityType.getAttributes()) {
 				searchAttributes.add(
-						new SearchAttribute(entityType.getName(), attr.getName(), attr.getValueType().toString(), "*"));
+						new SearchAttribute(ServiceUtils.workaroundForTypeMapping(entityType), attr.getName(), attr.getValueType().toString(), "*"));
 			}
 		}
 
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchParamParser.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchParamParser.java
index 0c917e5..c28465a 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchParamParser.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchParamParser.java
@@ -11,16 +11,23 @@
 
 package org.eclipse.mdm.businessobjects.control;
 
+import java.time.LocalDateTime;
+import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.StringTokenizer;
 
+import javax.mail.search.SearchException;
+
 import org.eclipse.mdm.api.base.model.ValueType;
 import org.eclipse.mdm.api.base.query.Attribute;
 import org.eclipse.mdm.api.base.query.Condition;
 import org.eclipse.mdm.api.base.query.EntityType;
 import org.eclipse.mdm.api.base.query.Filter;
 import org.eclipse.mdm.api.base.query.Operation;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import com.google.common.base.Strings;
 
 /**
  * Class for parsing the filter strings.
@@ -53,6 +60,9 @@
 	 */
 	public static Filter parseFilterString(List<EntityType> possibleSearchAttrs, String filterString)
 			throws IllegalArgumentException {
+		if (Strings.isNullOrEmpty(filterString)) {
+			return Filter.and();
+		}
 		SearchFilterBuilder filterBuilder = new SearchFilterBuilder();
 		while (filterString.contains(AND_DELIMITER) || filterString.contains(OR_DELIMITER)) {
 			int andIndex = filterString.indexOf(AND_DELIMITER) >= 0 ? filterString.indexOf(AND_DELIMITER)
@@ -195,7 +205,7 @@
 	private static Attribute validateAttribute(String entityName, String attrName, List<EntityType> possibleSearchAttrs)
 			throws IllegalArgumentException {
 		for (EntityType entityType : possibleSearchAttrs) {
-			if (entityType.getName().equals(entityName)) {
+			if (ServiceUtils.workaroundForTypeMapping(entityType).equals(entityName)) {
 				List<Attribute> attributes = entityType.getAttributes();
 				for (Attribute attribute : attributes) {
 					if (attribute.getName().equals(attrName)) {
@@ -237,8 +247,15 @@
 			ret = Integer.valueOf(valueAsString);
 		} else if (ValueType.SHORT.equals(valueType)) {
 			ret = Short.valueOf(valueAsString);
+		} else if (ValueType.DATE.equals(valueType)) {
+			try {
+				ret = LocalDateTime.parse(valueAsString);
+			} catch (DateTimeParseException e) {
+				throw new IllegalArgumentException("Unsupported value for date: '" + valueAsString 
+						+ "'. Expected format: '2007-12-03T10:15:30'");
+			}
 		} else {
-			throw new IllegalArgumentException("Unsupported value type: " + valueAsString.toString());
+			throw new IllegalArgumentException("Unsupported value type: " + valueType.toString());
 		}
 		return ret;
 	}
@@ -269,5 +286,4 @@
 		}
 		return operation;
 	}
-
 }
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ServiceUtils.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ServiceUtils.java
index 774695a..1a946a0 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ServiceUtils.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ServiceUtils.java
@@ -53,7 +53,7 @@
 		EntityType et = mm.getEntityType(parentType);
 
 		String idAttributeName = et.getIDAttribute().getName();
-		String matcher = parentType.getSimpleName() + "." + idAttributeName + " eq (\\d+)";
+		String matcher = workaroundForTypeMapping(et) + "." + idAttributeName + " eq (\\d+)";
 		return filter.matches(matcher);
 	}
 
@@ -72,7 +72,7 @@
 		EntityType et = mm.getEntityType(parentType);
 
 		String idAttributeName = et.getIDAttribute().getName();
-		return Long.valueOf(filter.replace(parentType.getSimpleName() + "." + idAttributeName + " eq ", ""));
+		return Long.valueOf(filter.replace(workaroundForTypeMapping(et) + "." + idAttributeName + " eq ", ""));
 	}
 
 
@@ -104,5 +104,24 @@
 		}
 		return oSS.get();
 	}
-
+	
+	/**
+	 * Simple workaround for naming mismatch between Adapter and Business object names.
+	 * @param entityType entity type
+	 * @return MDM business object name
+	 */
+	public static String workaroundForTypeMapping(EntityType entityType) {
+		switch (entityType.getName()) {
+		case "StructureLevel":
+			return "Pool";
+		case "MeaResult":
+			return "Measurement";
+		case "SubMatrix":
+			return "ChannelGroup";
+		case "MeaQuantity":
+			return "Channel";
+		default: 
+			return entityType.getName();
+		}
+	}
 }
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/boundary/QueryResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/boundary/QueryResource.java
new file mode 100644
index 0000000..460ee7b
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/boundary/QueryResource.java
@@ -0,0 +1,62 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.boundary;

+

+import java.util.List;

+

+import javax.ejb.EJB;

+import javax.ws.rs.Consumes;

+import javax.ws.rs.POST;

+import javax.ws.rs.Path;

+import javax.ws.rs.Produces;

+import javax.ws.rs.WebApplicationException;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+import javax.ws.rs.core.Response.Status;

+

+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;

+import org.eclipse.mdm.query.entity.QueryRequest;

+import org.eclipse.mdm.query.entity.QueryResult;

+import org.eclipse.mdm.query.entity.SuggestionRequest;

+import org.eclipse.mdm.query.entity.SuggestionResponse;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+@Path("/")

+public class QueryResource {

+

+	@EJB

+	private QueryService queryService;

+	

+	@POST

+	@Path("query")

+	@Produces(MediaType.APPLICATION_JSON)

+	@Consumes(MediaType.APPLICATION_JSON)

+	public QueryResult query(QueryRequest request) {	

+		return new QueryResult(queryService.queryRows(request));

+	}

+	

+	@POST

+	@Produces(MediaType.APPLICATION_JSON)

+	@Consumes(MediaType.APPLICATION_JSON)

+	@Path("suggestions")

+	public Response getSearchAttributes(SuggestionRequest suggestionRequest) {

+		try {

+			List<String> suggestions = queryService.getSuggestions(suggestionRequest);

+			return ServiceUtils.toResponse(new SuggestionResponse(suggestions), Status.OK);

+		} catch (RuntimeException e) {

+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);

+		}

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/boundary/QueryService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/boundary/QueryService.java
new file mode 100644
index 0000000..3906dc4
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/boundary/QueryService.java
@@ -0,0 +1,188 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.boundary;

+

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Objects;

+import java.util.Optional;

+import java.util.stream.Collectors;

+

+import javax.ejb.EJB;

+import javax.ejb.Stateless;

+import javax.inject.Inject;

+

+import org.eclipse.mdm.api.base.model.Entity;

+import org.eclipse.mdm.api.base.query.Attribute;

+import org.eclipse.mdm.api.base.query.DataAccessException;

+import org.eclipse.mdm.api.base.query.EntityType;

+import org.eclipse.mdm.api.base.query.Filter;

+import org.eclipse.mdm.api.base.query.ModelManager;

+import org.eclipse.mdm.api.base.query.Result;

+import org.eclipse.mdm.api.base.query.SearchService;

+import org.eclipse.mdm.api.dflt.EntityManager;

+import org.eclipse.mdm.businessobjects.control.SearchParamParser;

+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;

+import org.eclipse.mdm.connector.boundary.ConnectorService;

+import org.eclipse.mdm.connector.boundary.ConnectorServiceException;

+import org.eclipse.mdm.property.GlobalProperty;

+import org.eclipse.mdm.query.entity.QueryRequest;

+import org.eclipse.mdm.query.entity.Row;

+import org.eclipse.mdm.query.entity.SourceFilter;

+import org.eclipse.mdm.query.entity.SuggestionRequest;

+import org.eclipse.mdm.query.util.Util;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+@Stateless

+public class QueryService {

+	

+	private static final Logger LOG = LoggerFactory.getLogger(QueryService.class); 

+	

+	@Inject

+	@GlobalProperty(value = "businessobjects.query.maxresultspersource")

+	private String maxResultsPerSource = "1001";

+	

+	@EJB

+	ConnectorService connectorService;

+	

+	

+	public List<Row> queryRows(QueryRequest request) {

+		List<Row> rows = new ArrayList<>();

+		

+		for (SourceFilter filter : request.getFilters()) {

+			try {

+				EntityManager em = this.connectorService.getEntityManagerByName(filter.getSourceName());

+			

+				rows.addAll(queryRowsForSource(em, request.getResultType(), request.getColumns(), filter.getFilter(), filter.getSearchString()));

+			}

+			catch (ConnectorServiceException e) {

+				LOG.warn("Could not retrieve EntityManager for environment '"  + filter.getSourceName() + "'!", e);

+			}

+			catch (Exception e) {

+				LOG.warn("Could not retrieve query results for environment '"  + filter.getSourceName() + "': " + e.getMessage(), e);

+			}

+		}

+			

+		return rows;

+	}

+

+	public List<String> getSuggestions(SuggestionRequest suggestionRequest) {

+		

+		List<String> suggestions = new ArrayList<>();

+		
+		for (String envName: suggestionRequest.getSourceNames()) {
+			

+			EntityManager em = this.connectorService.getEntityManagerByName(envName);

+			Optional<ModelManager> mm = em.getModelManager();

+			

+			if (mm.isPresent()) {

+	

+				try {

+					EntityType entityType = mm.get().getEntityType(suggestionRequest.getType());

+				

+					Attribute attr = entityType.getAttribute(suggestionRequest.getAttrName());

+				

+					suggestions.addAll(mm.get().createQuery()

+						.select(attr)

+						.group(attr)

+						.fetch()

+						.stream()

+						.map(r -> Objects.toString(r.getValue(attr).extract()))

+						.collect(Collectors.toList()));

+					

+				} catch (DataAccessException | IllegalArgumentException e) {

+					LOG.warn("Cannot retreive suggestions " + suggestionRequest + " for Environment + " + envName + "!", e);

+				}
+			}

+		}

+		return suggestions;
+	}

+	

+	List<Row> queryRowsForSource(EntityManager em, String resultEntity, List<String> columns, String filterString, String searchString) throws DataAccessException {

+

+		ModelManager modelManager = getModelManager(em);

+		

+		SearchService searchService = ServiceUtils.getSearchService(em);

+		

+		Class<? extends Entity> resultType = getEntityClassByNameType(searchService, resultEntity);

+

+		List<EntityType> searchableTypes = searchService.listEntityTypes(resultType);

+		List<Attribute> attributes = columns.stream()

+				.map(c -> getAttribute(searchableTypes, c))

+				.filter(Optional::isPresent)

+			    .map(Optional::get)

+				.collect(Collectors.toList());

+

+		Filter filter = SearchParamParser.parseFilterString(searchableTypes, filterString);

+		

+		List<Result> result = searchService.fetchResults(resultType, attributes, filter, searchString);

+		

+		return Util.convertResultList(result.subList(0, Math.min(result.size(), getMaxResultsPerSource())), resultType, modelManager.getEntityType(resultType));

+	}

+	

+	private ModelManager getModelManager(EntityManager em) {

+		Optional<ModelManager> mm = em.getModelManager();

+		if(!mm.isPresent()) {

+			throw new IllegalStateException("neccessary ModelManager is not available");

+		}

+		return mm.get();

+	}

+	

+	private Optional<Attribute> getAttribute(List<EntityType> searchableTypes, String c) {

+		String[] parts = c.split("\\.");

+		

+		if (parts.length != 2) {

+			throw new IllegalArgumentException("Cannot parse attribute " + c + "!");

+		}

+		

+		String type = parts[0];

+		String attributeName = parts[1];

+		

+		Optional<EntityType> entityType = searchableTypes.stream()

+				.filter(e -> ServiceUtils.workaroundForTypeMapping(e).equalsIgnoreCase(type))

+				.findFirst();

+		

+		if (entityType.isPresent()) {

+			return entityType.get().getAttributes().stream()

+				.filter(a -> a.getName().equalsIgnoreCase(attributeName))

+				.findFirst();

+		} else {

+			return Optional.empty();

+		}

+	}

+

+	private Class<? extends Entity> getEntityClassByNameType(SearchService s, String name) {

+

+		for (Class<? extends Entity> entityClass : s.listSearchableTypes()) {

+			if (entityClass.getSimpleName().equalsIgnoreCase(name)) {

+				return entityClass;

+			}

+		}

+		throw new IllegalArgumentException("Invalid Entity '" + name + "'. Allowed values are: " 

+				+ s.listSearchableTypes().stream().map(Class::getSimpleName).collect(Collectors.joining(", ")));

+	}

+	

+	private int getMaxResultsPerSource() {

+		try {

+			return Integer.parseInt(maxResultsPerSource);

+		}

+		catch (NumberFormatException e) {

+			return 1001;

+		}

+	}

+	

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/Column.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/Column.java
new file mode 100644
index 0000000..2fc0c2f
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/Column.java
@@ -0,0 +1,101 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.entity;

+

+import java.util.Objects;

+

+import com.fasterxml.jackson.annotation.JsonInclude;

+import com.fasterxml.jackson.annotation.JsonInclude.Include;

+

+import jersey.repackaged.com.google.common.base.MoreObjects;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+@JsonInclude(Include.NON_NULL)

+public class Column {

+	private String type;

+	private String attribute;

+	private String value;

+	private String unit;

+	

+	public Column(String type, String attribute, String value, String unit) {

+		this.type = type;

+		this.attribute = attribute;

+		this.value = value;

+		this.unit = unit;

+	}

+

+	public String getType() {

+		return type;

+	}

+

+	public void setType(String type) {

+		this.type = type;

+	}

+

+	public String getAttribute() {

+		return attribute;

+	}

+

+	public void setAttribute(String attribute) {

+		this.attribute = attribute;

+	}

+

+	public String getValue() {

+		return value;

+	}

+

+	public void setValue(String value) {

+		this.value = value;

+	}

+

+	public String getUnit() {

+		return unit;

+	}

+

+	public void setUnit(String unit) {

+		this.unit = unit;

+	}

+	

+	@Override

+	public int hashCode() {

+		return Objects.hash(type, attribute, value, unit);

+	}

+	

+	@Override

+	public boolean equals(Object obj) {

+		if (obj == null) {

+			return false;

+		}

+		

+		if (getClass() != obj.getClass()) {

+			return false;

+		}

+		final Column other = (Column) obj;

+		return Objects.equals(this.type, other.type)

+				&& Objects.equals(this.attribute, other.attribute)

+				&& Objects.equals(this.value, other.value)

+				&& Objects.equals(this.unit, other.unit);

+	}

+	

+	@Override

+	public String toString() {

+		return MoreObjects.toStringHelper(Column.class)

+				.add("type", type)

+				.add("attribute", attribute)

+				.add("value", value)

+				.add("unit", unit)

+				.toString();

+	}

+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/EntityId.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/EntityId.java
new file mode 100644
index 0000000..c98d478
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/EntityId.java
@@ -0,0 +1,48 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.entity;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+public class EntityId {

+	private String source;

+	private String type;

+	private long id;

+

+	public String getSource() {

+		return source;

+	}

+

+	public void setSource(String source) {

+		this.source = source;

+	}

+

+	public String getType() {

+		return type;

+	}

+

+	public void setType(String type) {

+		this.type = type;

+	}

+

+	public long getId() {

+		return id;

+	}

+

+	public void setId(long id) {

+		this.id = id;

+	}

+	

+	

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/LoadRequest.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/LoadRequest.java
new file mode 100644
index 0000000..cd6700e
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/LoadRequest.java
@@ -0,0 +1,39 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.entity;

+

+import java.util.List;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+public class LoadRequest {

+	private List<EntityId> entityIds;

+	private List<String> columns;

+	

+	public List<EntityId> getEntityIds() {

+		return entityIds;

+	}

+	

+	public void setEntityIds(List<EntityId> entityIds) {

+		this.entityIds = entityIds;

+	}

+	

+	public List<String> getColumns() {

+		return columns;

+	}

+	

+	public void setColumns(List<String> columns) {

+		this.columns = columns;

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/QueryRequest.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/QueryRequest.java
new file mode 100644
index 0000000..d9e3a26
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/QueryRequest.java
@@ -0,0 +1,47 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.entity;

+

+import java.util.List;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+public class QueryRequest {

+

+	private List<SourceFilter> filters;

+	private List<String> columns;

+	private String resultType;

+	

+	public List<SourceFilter> getFilters() {

+		return filters;

+	}

+	

+	public void setFilters(List<SourceFilter> filters) {

+		this.filters = filters;

+	}

+	

+	public List<String> getColumns() {

+		return columns;

+	}

+	public void setColumns(List<String> columns) {

+		this.columns = columns;

+	}

+	public String getResultType() {

+		return resultType;

+	}

+	

+	public void setResultType(String resultType) {

+		this.resultType = resultType;

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/QueryResult.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/QueryResult.java
new file mode 100644
index 0000000..dd0b08b
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/QueryResult.java
@@ -0,0 +1,34 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.entity;

+

+import java.util.List;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+public class QueryResult {

+	private List<Row> rows;

+

+	public QueryResult(List<Row> rows) {

+		this.rows = rows;

+	}

+

+	public List<Row> getRows() {

+		return rows;

+	}

+

+	public void setRows(List<Row> rows) {

+		this.rows = rows;

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/Row.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/Row.java
new file mode 100644
index 0000000..9148cfd
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/Row.java
@@ -0,0 +1,103 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.entity;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+import java.util.Objects;

+

+import jersey.repackaged.com.google.common.base.MoreObjects;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+public class Row {

+

+	private String source;

+	private String type;

+	private Long id;

+	

+	private List<Column> columns = new ArrayList<>();

+	

+	public void setSource(String source) {

+		this.source = source;

+	}

+	

+	public String getSource() {

+		return source;

+	}

+	

+	public void setType(String type) {

+		this.type = type;

+	}

+	

+	public String getType() {

+		return type;

+	}

+	

+	public Long getId() {

+		return id;

+	}

+

+	public void setId(Long id) {

+		this.id = id;

+	}

+	

+	public void addColumn(Column col) {

+		columns.add(col);

+	}

+	

+	public List<Column> getColumns() {

+		return columns;

+	}

+

+	public void setColumns(List<Column> columns) {

+		this.columns = columns;

+	}

+	

+	public void addColumns(Collection<? extends Column> columns) {

+		this.columns.addAll(columns);

+	}

+	

+	@Override

+	public int hashCode() {

+		return Objects.hash(source, type, id, columns);

+	}

+	

+	@Override

+	public boolean equals(Object obj) {

+		if (obj == null) {

+			return false;

+		}

+		

+		if (getClass() != obj.getClass()) {

+			return false;

+		}

+		final Row other = (Row) obj;

+		return Objects.equals(this.source, other.source)

+				&& Objects.equals(this.type, other.type)

+				&& Objects.equals(this.id, other.id)

+				&& Objects.equals(this.columns, other.columns);

+	}

+	

+	@Override

+	public String toString() {

+		return MoreObjects.toStringHelper(Row.class)

+				.add("source", source)

+				.add("type", type)

+				.add("id", id)

+				.add("columns", columns)

+				.toString();

+	}

+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SourceFilter.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SourceFilter.java
new file mode 100644
index 0000000..bdb6c95
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SourceFilter.java
@@ -0,0 +1,46 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.entity;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+public class SourceFilter {

+	private String sourceName;

+	private String filter;

+	private String searchString;

+	

+	public String getSourceName() {

+		return sourceName;

+	}

+

+	public void setSourceName(String sourceName) {

+		this.sourceName = sourceName;

+	}

+

+	public String getFilter() {

+		return filter;

+	}

+

+	public void setFilter(String filter) {

+		this.filter = filter;

+	}

+	

+	public String getSearchString() {

+		return searchString;

+	}

+	

+	public void setSearchString(String searchString) {

+		this.searchString = searchString;

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SuggestionRequest.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SuggestionRequest.java
new file mode 100644
index 0000000..f3b95c0
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SuggestionRequest.java
@@ -0,0 +1,65 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.entity;

+

+import java.util.List;

+

+import javax.xml.bind.annotation.XmlAccessType;

+import javax.xml.bind.annotation.XmlAccessorType;

+import javax.xml.bind.annotation.XmlRootElement;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+@XmlRootElement

+@XmlAccessorType(XmlAccessType.FIELD)

+public class SuggestionRequest {

+	

+	private List<String> sourceNames;

+	private String type;

+	private String attrName;

+	

+	public SuggestionRequest() {

+		// empty no arg constructor

+	}

+	public SuggestionRequest (List<String> sourceNames, String type, String name) {

+		this.sourceNames = sourceNames;

+		this.type = type;

+		this.attrName = name;

+	}

+

+	

+	public void setSourceNames(List<String> sourceNames) {

+		this.sourceNames = sourceNames;

+	}

+

+	public void setType(String type) {

+		this.type = type;

+	}

+

+	public void setAttrName(String attrName) {

+		this.attrName = attrName;

+	}

+

+	public List<String> getSourceNames() {

+		return sourceNames;

+	}

+

+	public String getType() {

+		return type;

+	}

+

+	public String getAttrName() {

+		return attrName;

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SuggestionResponse.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SuggestionResponse.java
new file mode 100644
index 0000000..a7fff8c
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SuggestionResponse.java
@@ -0,0 +1,49 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.entity;

+

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.List;

+

+import javax.xml.bind.annotation.XmlAccessType;

+import javax.xml.bind.annotation.XmlAccessorType;

+import javax.xml.bind.annotation.XmlRootElement;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+@XmlRootElement

+@XmlAccessorType(XmlAccessType.FIELD)

+public class SuggestionResponse {

+

+	

+	/** transferable data content */

+	private List<String> data;

+	

+	/**

+	 * Constructor

+	 * @param searchDefinitions list of {@link Suggestion}s to transfer

+	 */

+	public SuggestionResponse(List<String> suggestions) {

+		this.data = new ArrayList<>(suggestions);

+	}

+	

+	public SuggestionResponse() {

+		this.data = new ArrayList<>();

+	}

+	

+	public List<String> getData() {

+		return Collections.unmodifiableList(this.data);

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/util/Util.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/util/Util.java
new file mode 100644
index 0000000..13d0d32
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/util/Util.java
@@ -0,0 +1,69 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.util;

+

+import java.util.ArrayList;

+import java.util.Collection;

+import java.util.List;

+import java.util.Objects;

+

+import org.eclipse.mdm.api.base.model.Entity;

+import org.eclipse.mdm.api.base.model.Value;

+import org.eclipse.mdm.api.base.query.EntityType;

+import org.eclipse.mdm.api.base.query.Record;

+import org.eclipse.mdm.api.base.query.Result;

+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;

+import org.eclipse.mdm.query.entity.Column;

+import org.eclipse.mdm.query.entity.Row;

+

+import com.google.common.base.Strings;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+public class Util {

+

+	private Util() {

+		

+	}

+	

+	public static List<Row> convertResultList(Collection<Result> results, Class<? extends Entity> resultEntityClass, EntityType type) {

+		List<Row> rows = new ArrayList<>();

+		results.forEach(row -> rows.add(convertResult(row, resultEntityClass, type)));

+		return rows;

+	}

+

+	public static Row convertResult(Result result, Class<? extends Entity> resultEntityClass, EntityType type) {

+		Row row = new Row();

+		row.setSource(type.getSourceName());

+		row.setType(resultEntityClass.getSimpleName());

+		row.setId(result.getRecord(type).getID());

+		result.forEach(record -> row.addColumns(convertRecord(record)));

+		return row;

+	}

+

+	public static List<Column> convertRecord(Record record) {		

+		List<Column> columns = new ArrayList<>();

+		record.getValues().values().forEach(value -> columns.add(convertColumn(record, value)));

+		return columns;

+	}

+

+	public static  Column convertColumn(Record record, Value value) {

+		return new Column(

+				ServiceUtils.workaroundForTypeMapping(record.getEntityType()),

+				value.getName(), 

+				Strings.emptyToNull(Objects.toString(value.extract())), 

+				Strings.emptyToNull(value.getUnit()));

+	}

+

+}

diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/PoolServiceTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/PoolServiceTest.java
new file mode 100644
index 0000000..4c697b5
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/PoolServiceTest.java
@@ -0,0 +1,159 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.businessobjects.boundary;

+

+import static org.mockito.Mockito.doThrow;

+import static org.mockito.Mockito.mock;

+import static org.mockito.Mockito.verify;

+import static org.mockito.Mockito.verifyNoMoreInteractions;

+import static org.mockito.Mockito.verifyZeroInteractions;

+import static org.mockito.Mockito.when;

+

+import java.util.Arrays;

+import java.util.Optional;

+

+import org.eclipse.mdm.api.base.model.ValueType;

+import org.eclipse.mdm.api.base.query.Attribute;

+import org.eclipse.mdm.api.base.query.DataAccessException;

+import org.eclipse.mdm.api.base.query.EntityType;

+import org.eclipse.mdm.api.base.query.ModelManager;

+import org.eclipse.mdm.api.dflt.EntityManager;

+import org.eclipse.mdm.api.dflt.model.Pool;

+import org.eclipse.mdm.api.dflt.model.Project;

+import org.eclipse.mdm.businessobjects.control.I18NActivity;

+import org.eclipse.mdm.businessobjects.control.MDMEntityAccessException;

+import org.eclipse.mdm.businessobjects.control.NavigationActivity;

+import org.eclipse.mdm.businessobjects.control.SearchActivity;

+import org.eclipse.mdm.connector.boundary.ConnectorService;

+import org.junit.Before;

+import org.junit.Test;

+import org.mockito.Mockito;

+

+public class PoolServiceTest {

+

+	EntityManager em = Mockito.mock(EntityManager.class);

+	ModelManager mm = mockModelManager();

+	ConnectorService connectorService = Mockito.mock(ConnectorService.class);

+	SearchActivity searchActivity = Mockito.mock(SearchActivity.class);

+	NavigationActivity navigationActivity = Mockito.mock(NavigationActivity.class);

+	I18NActivity i18nActivity = Mockito.mock(I18NActivity.class);

+	

+	PoolService service = new PoolService(connectorService, searchActivity, navigationActivity, i18nActivity);

+	

+	@Before

+	public void init() {

+		when(connectorService.getEntityManagerByName("MDMTEST")).thenReturn(em);

+		when(em.getModelManager()).thenReturn(Optional.of(mm));

+	}

+	

+	@Test

+	public void testGetPool() throws DataAccessException {

+		service.getPool("MDMTEST", 1L);

+		

+		verify(em).load(Pool.class, 1L);

+		verifyNoMoreInteractions(searchActivity);

+	}

+

+	@Test

+	public void testGetPoolsEmptyFilter() throws DataAccessException {

+		service.getPools("MDMTEST", "");

+		

+		verify(em).loadAll(Mockito.any());

+		verifyNoMoreInteractions(searchActivity);

+	}

+	

+	@Test

+	public void testGetPoolsNullFilter() throws DataAccessException {

+		service.getPools("MDMTEST", null);

+		

+		verify(em).loadAll(Mockito.any());

+		verifyNoMoreInteractions(searchActivity);

+	}

+	

+	@Test(expected = MDMEntityAccessException.class)

+	public void testGetPoolsWrongEnvironment() {

+		doThrow(MDMEntityAccessException.class).when(connectorService).getEntityManagerByName("wrongEnvironment");

+		

+		service.getPools("wrongEnvironment", "Pool.Name eq crash");

+	}

+	

+	@Test

+	public void testGetPoolsParentFilter() {

+		service.getPools("MDMTEST", "Project.Id eq 4711");

+		

+		verify(navigationActivity).getPools("MDMTEST", 4711L);

+		verifyZeroInteractions(searchActivity);

+	}

+	

+	@Test

+	public void testGetPools() {

+		service.getPools("MDMTEST", "Pool.Name eq crash");

+		

+		verify(searchActivity).search(em, Pool.class, "Pool.Name eq crash");

+	}

+	

+	@Test

+	public void testGetSearchAttributes() {

+		service.getSearchAttributes("MDMTEST");

+		

+		verify(searchActivity).listAvailableAttributes(em, Pool.class);

+	}

+

+	@Test

+	public void testLocalizeAttributes() {

+		service.localizeAttributes("MDMTEST");

+		verify(i18nActivity).localizeAttributes("MDMTEST", Pool.class);

+	}

+	

+	@Test

+	public void testLocalizeType() {

+		service.localizeType("MDMTEST");

+		verify(i18nActivity).localizeType("MDMTEST", Pool.class);

+	}

+

+	private ModelManager mockModelManager() {

+		

+		Attribute projectId = mock(Attribute.class);

+		when(projectId.getName()).thenReturn("Id");

+		

+		EntityType project = mock(EntityType.class);

+		when(project.getSourceName()).thenReturn("MDMTEST");

+		when(project.getName()).thenReturn("Project");

+		when(project.getAttributes()).thenReturn(Arrays.asList(projectId));

+		when(project.getIDAttribute()).thenReturn(projectId);

+		

+		Attribute poolId = mock(Attribute.class);

+		when(poolId.getName()).thenReturn("Id");

+		when(poolId.getValueType()).thenReturn(ValueType.LONG);

+		

+		Attribute poolName = mock(Attribute.class);

+		when(poolName.getName()).thenReturn("Name");

+		when(poolName.getValueType()).thenReturn(ValueType.STRING);

+		

+		EntityType pool = mock(EntityType.class);

+		when(pool.getSourceName()).thenReturn("MDMTEST");

+		when(pool.getName()).thenReturn("Pool");

+		when(pool.getAttributes()).thenReturn(Arrays.asList(poolId, poolName));

+		when(pool.getAttribute("Name")).thenReturn(poolName);

+		when(pool.getIDAttribute()).thenReturn(poolId);

+		

+		when(poolId.getEntityType()).thenReturn(pool);

+		when(poolName.getEntityType()).thenReturn(pool);

+		

+		ModelManager mm = mock(ModelManager.class);

+		when(mm.getEntityType(Project.class)).thenReturn(project);

+		when(mm.getEntityType("Project")).thenReturn(project);

+		when(mm.getEntityType(Pool.class)).thenReturn(pool);

+		when(mm.getEntityType("Pool")).thenReturn(pool);

+

+		return mm;

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/ProjectServiceTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/ProjectServiceTest.java
new file mode 100644
index 0000000..0233bc1
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/ProjectServiceTest.java
@@ -0,0 +1,100 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.businessobjects.boundary;

+

+import static org.mockito.Mockito.doThrow;

+import static org.mockito.Mockito.verify;

+import static org.mockito.Mockito.verifyNoMoreInteractions;

+import static org.mockito.Mockito.when;

+

+import org.eclipse.mdm.api.base.query.DataAccessException;

+import org.eclipse.mdm.api.dflt.EntityManager;

+import org.eclipse.mdm.api.dflt.model.Project;

+import org.eclipse.mdm.businessobjects.control.I18NActivity;

+import org.eclipse.mdm.businessobjects.control.MDMEntityAccessException;

+import org.eclipse.mdm.businessobjects.control.SearchActivity;

+import org.eclipse.mdm.connector.boundary.ConnectorService;

+import org.junit.Before;

+import org.junit.Test;

+import org.mockito.Mockito;

+

+public class ProjectServiceTest {

+

+	EntityManager em = Mockito.mock(EntityManager.class);

+	ConnectorService connectorService = Mockito.mock(ConnectorService.class);

+	SearchActivity searchActivity = Mockito.mock(SearchActivity.class);

+	I18NActivity i18nActivity = Mockito.mock(I18NActivity.class);

+	

+	ProjectService service = new ProjectService(connectorService, searchActivity, i18nActivity);

+	

+	@Before

+	public void init() {

+		when(connectorService.getEntityManagerByName("MDMTEST")).thenReturn(em);

+	}

+	

+	@Test

+	public void testGetProject() throws DataAccessException {

+		service.getProject("MDMTEST", 1L);

+		

+		verify(em).load(Project.class, 1L);

+		verifyNoMoreInteractions(searchActivity);

+	}

+

+	@Test

+	public void testGetProjectsEmptyFilter() throws DataAccessException {

+		service.getProjects("MDMTEST", "");

+		

+		verify(em).loadAll(Mockito.any());

+		verifyNoMoreInteractions(searchActivity);

+	}

+	

+	@Test

+	public void testGetProjectsNullFilter() throws DataAccessException {

+		service.getProjects("MDMTEST", null);

+		

+		verify(em).loadAll(Mockito.any());

+		verifyNoMoreInteractions(searchActivity);

+	}

+	

+	@Test(expected = MDMEntityAccessException.class)

+	public void testGetProjectsWrongEnvironment() {

+		doThrow(MDMEntityAccessException.class).when(connectorService).getEntityManagerByName("wrongEnvironment");

+		

+		service.getProjects("wrongEnvironment", "Project.Name eq crash");

+	}

+	

+	@Test

+	public void testGetProjects() {

+		service.getProjects("MDMTEST", "Project.Name eq crash");

+		

+		verify(searchActivity).search(em, Project.class, "Project.Name eq crash");

+	}

+	

+	@Test

+	public void testGetSearchAttributes() {

+		service.getSearchAttributes("MDMTEST");

+		

+		verify(searchActivity).listAvailableAttributes(em, Project.class);

+	}

+

+	@Test

+	public void testLocalizeAttributes() {

+		service.localizeAttributes("MDMTEST");

+		verify(i18nActivity).localizeAttributes("MDMTEST", Project.class);

+	}

+	

+	@Test

+	public void testLocalizeType() {

+		service.localizeType("MDMTEST");

+		verify(i18nActivity).localizeType("MDMTEST", Project.class);

+	}

+

+}

diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/query/boundary/QueryServiceTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/query/boundary/QueryServiceTest.java
new file mode 100644
index 0000000..0726ac9
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/query/boundary/QueryServiceTest.java
@@ -0,0 +1,290 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.boundary;

+

+import static org.assertj.core.api.Assertions.assertThat;

+import static org.mockito.Mockito.doThrow;

+import static org.mockito.Mockito.mock;

+import static org.mockito.Mockito.when;

+

+import java.util.Arrays;

+import java.util.List;

+import java.util.Optional;

+

+import org.assertj.core.groups.Tuple;

+import org.eclipse.mdm.api.base.model.Test;

+import org.eclipse.mdm.api.base.model.ValueType;

+import org.eclipse.mdm.api.base.query.Attribute;

+import org.eclipse.mdm.api.base.query.DataAccessException;

+import org.eclipse.mdm.api.base.query.EntityType;

+import org.eclipse.mdm.api.base.query.ModelManager;

+import org.eclipse.mdm.api.base.query.Query;

+import org.eclipse.mdm.api.base.query.Record;

+import org.eclipse.mdm.api.base.query.Result;

+import org.eclipse.mdm.api.base.query.SearchService;

+import org.eclipse.mdm.api.dflt.EntityManager;

+import org.eclipse.mdm.api.dflt.model.Pool;

+import org.eclipse.mdm.connector.boundary.ConnectorService;

+import org.eclipse.mdm.connector.boundary.ConnectorServiceException;

+import org.eclipse.mdm.query.entity.Column;

+import org.eclipse.mdm.query.entity.QueryRequest;

+import org.eclipse.mdm.query.entity.Row;

+import org.eclipse.mdm.query.entity.SourceFilter;

+import org.eclipse.mdm.query.entity.SuggestionRequest;

+import org.mockito.Mockito;

+

+

+public class QueryServiceTest {

+

+	

+	@org.junit.Test

+	public void testQuery() throws DataAccessException {

+		

+		ModelManager mm = mockModelManager();

+		EntityManager em = mockEntityManager(mm);

+		

+		ConnectorService connectorService = Mockito.mock(ConnectorService.class);

+		when(connectorService.getEntityManagerByName("env1")).thenReturn(em);

+		

+		QueryService queryService = new QueryService();

+		queryService.connectorService = connectorService;

+		

+		SourceFilter filter = new SourceFilter();

+		filter.setSourceName("env1");

+		filter.setFilter("Test.Name lk *");

+		

+		QueryRequest request = new QueryRequest();

+		request.setResultType("Test");

+		request.setColumns(Arrays.asList("Test.Id", "Test.Name"));

+		request.setFilters(Arrays.asList(filter));

+

+		Row expectedRow = new Row();

+		expectedRow.setSource("env1");

+		expectedRow.setType("Test");

+		expectedRow.setId(1L);

+		expectedRow.addColumns(Arrays.asList(

+				new Column("Test", "Id", "1", null), 

+				new Column("Test", "Name", "Test-Name", null)));

+		

+		assertThat(queryService.queryRows(request))

+			.contains(expectedRow);

+		

+	}

+

+	@org.junit.Test

+	public void testQueryMissingEnvironmentShouldByIgnored() throws DataAccessException {

+		

+		ModelManager mm = mockModelManager();

+		EntityManager em = mockEntityManager(mm);

+		

+		ConnectorService connectorService = Mockito.mock(ConnectorService.class);

+		when(connectorService.getEntityManagerByName("env1")).thenReturn(em);

+		doThrow(ConnectorServiceException.class).when(connectorService).getEntityManagerByName("env2");

+		

+		QueryService queryService = new QueryService();

+		queryService.connectorService = connectorService;

+		

+		

+		SourceFilter filterEnv1 = new SourceFilter();

+		filterEnv1.setSourceName("env1");

+		filterEnv1.setFilter("Test.Name lk *");

+		

+		SourceFilter filterEnv2 = new SourceFilter();

+		filterEnv2.setSourceName("env2");

+		filterEnv2.setFilter("Test.Name lk *");

+		

+		QueryRequest request = new QueryRequest();

+		request.setResultType("Test");

+		request.setColumns(Arrays.asList("Test.Id", "Test.Name", "Pool.Name"));

+		request.setFilters(Arrays.asList(filterEnv1, filterEnv2));

+

+		Row expectedRow = new Row();

+		expectedRow.setSource("env1");

+		expectedRow.setType("Test");

+		expectedRow.setId(1L);

+		expectedRow.addColumns(Arrays.asList(

+				new Column("Test", "Id", "1", null), 

+				new Column("Test", "Name", "Test-Name", null)));

+		

+		assertThat(queryService.queryRows(request))

+			.contains(expectedRow);

+	}

+	

+	@org.junit.Test

+	public void testQueryMissingEntitiesShouldBeIgnored() throws DataAccessException {

+		

+		ModelManager mm1 = mockModelManager();

+		EntityManager em1 = mockEntityManager(mm1);

+		

+		EntityType type = mockEntity("env2", "Test");

+		EntityType pool = mockEntity("env2", "Pool");

+		

+		ModelManager mm2 = mock(ModelManager.class);

+		when(mm2.getEntityType(Test.class)).thenReturn(type);

+		when(mm2.getEntityType("Test")).thenReturn(type);

+		when(mm2.getEntityType(Pool.class)).thenReturn(pool);

+		when(mm2.getEntityType("Pool")).thenReturn(pool);

+		

+		EntityManager em2 = mockEntityManager(mm2);

+		

+		SearchService ss = mock(SearchService.class);

+		EntityType test = mm2.getEntityType(Test.class);

+		when(ss.listEntityTypes(Mockito.eq(Test.class))).thenReturn(Arrays.asList(test, pool));

+		when(ss.listSearchableTypes()).thenReturn(Arrays.asList(Test.class));

+		

+		Record recordTest = new Record(mm2.getEntityType(Test.class));

+		recordTest.addValue(ValueType.LONG.create("Id", 1L));

+		recordTest.addValue(ValueType.STRING.create("Name", "Test-Name"));

+		

+		Record recordPool = new Record(mm2.getEntityType(Pool.class));

+		recordPool.addValue(ValueType.STRING.create("Name", "Pool-Name"));

+		Result result = new Result();

+		result.addRecord(recordPool);

+		result.addRecord(recordTest);

+		when(em2.getSearchService().get().fetchResults(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Arrays.asList(result));

+		

+		ConnectorService connectorService = Mockito.mock(ConnectorService.class);

+		when(connectorService.getEntityManagerByName("env1")).thenReturn(em1);

+		when(connectorService.getEntityManagerByName("env2")).thenReturn(em2);

+		

+		QueryService queryService = new QueryService();

+		queryService.connectorService = connectorService;

+		

+		SourceFilter filterEnv1 = new SourceFilter();

+		filterEnv1.setSourceName("env1");

+		filterEnv1.setFilter("Test.Name lk *");

+		

+		SourceFilter filterEnv2 = new SourceFilter();

+		filterEnv2.setSourceName("env2");

+		filterEnv2.setFilter("Test.Name lk *");

+		

+		QueryRequest request = new QueryRequest();

+		request.setResultType("Test");

+		request.setColumns(Arrays.asList("Test.Id", "Test.Name", "Pool.Name"));

+		request.setFilters(Arrays.asList(filterEnv1, filterEnv2));

+

+		Row expectedRowEnv1 = new Row();

+		expectedRowEnv1.setSource("env1");

+		expectedRowEnv1.setType("Test");

+		expectedRowEnv1.setId(1L);

+		expectedRowEnv1.addColumns(Arrays.asList(

+				new Column("Test", "Id", "1", null), 

+				new Column("Test", "Name", "Test-Name", null)));

+		

+		Row expectedRowEnv2 = new Row();

+		expectedRowEnv2.setSource("env2");

+		expectedRowEnv2.setType("Test");

+		expectedRowEnv2.setId(1L);

+		expectedRowEnv2.addColumns(Arrays.asList(

+				new Column("Test", "Id", "1", null), 

+				new Column("Test", "Name", "Test-Name", null),

+				new Column("Pool", "Name", "Pool-Name", null)

+				));

+		

+		List<Row> list = queryService.queryRows(request);

+		

+		assertThat(list)

+			.extracting("source", "type", "id")

+			.containsOnly(

+					new Tuple("env1", "Test", 1L), 

+					new Tuple("env2", "Test", 1L));

+			

+		assertThat(list.get(0).getColumns())

+			.containsOnlyElementsOf(expectedRowEnv1.getColumns());

+		

+		assertThat(list.get(1).getColumns())

+			.containsOnlyElementsOf(expectedRowEnv2.getColumns());

+	

+	}

+

+	private EntityType mockEntity(String sourceName, String name) {

+		Attribute attrId = mock(Attribute.class);

+		when(attrId.getName()).thenReturn("Id");

+		when(attrId.getValueType()).thenReturn(ValueType.LONG);

+		

+		Attribute attrName = mock(Attribute.class);

+		when(attrName.getName()).thenReturn("Name");

+		when(attrName.getValueType()).thenReturn(ValueType.STRING);

+		

+		EntityType entity = mock(EntityType.class);

+		when(entity.getSourceName()).thenReturn(sourceName);

+		when(entity.getName()).thenReturn(name);

+		when(entity.getAttributes()).thenReturn(Arrays.asList(attrId, attrName));

+		when(entity.getAttribute("Name")).thenReturn(attrName);

+		when(entity.getIDAttribute()).thenReturn(attrId);

+		

+		when(attrId.getEntityType()).thenReturn(entity);

+		when(attrName.getEntityType()).thenReturn(entity);

+		

+		return entity;

+	}

+	

+	private EntityManager mockEntityManager(ModelManager mm) throws DataAccessException {

+		

+		SearchService ss = mock(SearchService.class);

+		EntityType type = mm.getEntityType(Test.class);

+		when(ss.listEntityTypes(Mockito.eq(Test.class))).thenReturn(Arrays.asList(type));

+		when(ss.listSearchableTypes()).thenReturn(Arrays.asList(Test.class));

+		

+		Record record = new Record(mm.getEntityType(Test.class));

+		record.addValue(ValueType.LONG.create("Id", 1L));

+		record.addValue(ValueType.STRING.create("Name", "Test-Name"));

+		Result result = new Result();

+		result.addRecord(record);

+		when(ss.fetchResults(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(Arrays.asList(result));

+		

+		EntityManager em = mock(EntityManager.class);

+		when(em.getModelManager()).thenReturn(Optional.of(mm));

+		when(em.getSearchService()).thenReturn(Optional.of(ss));

+		return em;

+	}

+

+	private ModelManager mockModelManager() {

+		EntityType type = mockEntity("env1", "Test");

+		

+		ModelManager mm = mock(ModelManager.class);

+		when(mm.getEntityType(Test.class)).thenReturn(type);

+		when(mm.getEntityType("Test")).thenReturn(type);

+

+		return mm;

+	}

+

+	@org.junit.Test

+	public void testGetSuggestions() throws Exception {

+		ModelManager mm = mockModelManager();

+		EntityManager em = mockEntityManager(mm);

+		

+		Record record = new Record(mm.getEntityType(Test.class));

+		record.addValue(ValueType.LONG.create("Id", 1L));

+		record.addValue(ValueType.STRING.create("Name", "Test-Name"));

+		Result result = new Result();

+		result.addRecord(record);

+		

+		Query query = mock(Query.class);

+		when(query.select(Mockito.any(Attribute.class))).thenReturn(query);

+		when(query.group(Mockito.any(Attribute.class))).thenReturn(query);

+		when(query.fetch()).thenReturn(Arrays.asList(result));

+		when(mm.createQuery()).thenReturn(query);

+		

+		ConnectorService connectorService = Mockito.mock(ConnectorService.class);

+		when(connectorService.getEntityManagerByName("env1")).thenReturn(em);

+		

+		QueryService queryService = new QueryService();

+		queryService.connectorService = connectorService;

+		

+		SuggestionRequest request = new SuggestionRequest();

+		request.setSourceNames(Arrays.asList("env1"));

+		request.setType("Test");

+		request.setAttrName("Name");

+		

+		assertThat(queryService.getSuggestions(request)).contains("Test-Name");

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/query/boundary/QueryTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/query/boundary/QueryTest.java
new file mode 100644
index 0000000..90f541d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/query/boundary/QueryTest.java
@@ -0,0 +1,139 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Matthias Koller - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.query.boundary;

+

+import static org.eclipse.mdm.api.odsadapter.ODSEntityManagerFactory.PARAM_NAMESERVICE;

+import static org.eclipse.mdm.api.odsadapter.ODSEntityManagerFactory.PARAM_PASSWORD;

+import static org.eclipse.mdm.api.odsadapter.ODSEntityManagerFactory.PARAM_SERVICENAME;

+import static org.eclipse.mdm.api.odsadapter.ODSEntityManagerFactory.PARAM_USER;

+

+import java.io.IOException;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.Objects;

+

+import org.eclipse.mdm.api.base.ConnectionException;

+import org.eclipse.mdm.api.base.model.Value;

+import org.eclipse.mdm.api.base.query.DataAccessException;

+import org.eclipse.mdm.api.base.query.ModelManager;

+import org.eclipse.mdm.api.base.query.Record;

+import org.eclipse.mdm.api.base.query.Result;

+import org.eclipse.mdm.api.dflt.EntityManager;

+import org.eclipse.mdm.api.dflt.model.EntityFactory;

+import org.eclipse.mdm.api.odsadapter.ODSEntityManagerFactory;

+import org.eclipse.mdm.query.entity.Column;

+import org.eclipse.mdm.query.entity.Row;

+import org.junit.AfterClass;

+import org.junit.BeforeClass;

+import org.junit.Ignore;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+import com.fasterxml.jackson.core.JsonGenerationException;

+import com.fasterxml.jackson.databind.JsonMappingException;

+import com.fasterxml.jackson.databind.ObjectMapper;

+import com.fasterxml.jackson.databind.SerializationFeature;

+import com.google.common.base.Strings;

+

+public class QueryTest {

+

+	/*

+	 * ATTENTION:

+	 * ==========

+	 *

+	 * To run this test make sure the target service is running a

+	 * MDM default model and any database constraint which enforces

+	 * a relation of Test to a parent entity is deactivated!

+	 */

+

+	private static final Logger LOGGER = LoggerFactory.getLogger(QueryTest.class);

+

+	private static final String NAME_SERVICE = "corbaloc::1.2@%s:%s/NameService";

+

+	private static final String USER = "sa";

+	private static final String PASSWORD = "sa";

+

+	private static EntityManager entityManager;

+	private static EntityFactory entityFactory;

+

+	@BeforeClass

+	public static void setUpBeforeClass() throws ConnectionException {

+		String nameServiceHost = System.getProperty("host");

+		String nameServicePort = System.getProperty("port");

+		String serviceName = System.getProperty("service");

+

+		if(nameServiceHost == null || nameServiceHost.isEmpty()) {

+			throw new IllegalArgumentException("name service host is unknown: define system property 'host'");

+		}

+

+		nameServicePort = nameServicePort == null || nameServicePort.isEmpty() ? String.valueOf(2809) :  nameServicePort;

+		if(nameServicePort == null || nameServicePort.isEmpty()) {

+			throw new IllegalArgumentException("name service port is unknown: define system property 'port'");

+		}

+

+		if(serviceName == null || serviceName.isEmpty()) {

+			throw new IllegalArgumentException("service name is unknown: define system property 'service'");

+		}

+

+		Map<String, String> connectionParameters = new HashMap<>();

+		connectionParameters.put(PARAM_NAMESERVICE, String.format(NAME_SERVICE, nameServiceHost, nameServicePort));

+		connectionParameters.put(PARAM_SERVICENAME, serviceName + ".ASAM-ODS");

+		connectionParameters.put(PARAM_USER, USER);

+		connectionParameters.put(PARAM_PASSWORD, PASSWORD);

+

+		entityManager = new ODSEntityManagerFactory().connect(connectionParameters);

+		entityFactory = entityManager.getEntityFactory()

+				.orElseThrow(() -> new IllegalStateException("Entity manager factory not available."));

+	}

+

+	@AfterClass

+	public static void tearDownAfterClass() throws ConnectionException {

+		if(entityManager != null) {

+			entityManager.close();

+		}

+	}

+

+	@org.junit.Test

+	@Ignore

+	public void test() throws DataAccessException, JsonGenerationException, JsonMappingException, IOException {

+		ModelManager mm = entityManager.getModelManager().get();

+		

+		 List<Result> result = mm.createQuery()

+			.select(mm.getEntityType("Test").getAttribute("Id"))

+			.select(mm.getEntityType("Test").getAttribute("Name"))

+			.select(mm.getEntityType("TestStep").getAttribute("Id"))

+			.select(mm.getEntityType("TestStep").getAttribute("Name"))

+			.fetch();

+		 

+		 List<Row> rows = new ArrayList<>();

+		 

+		 for (Result r : result)

+		 {

+			 Row row = new Row();

+			 for (Record record : r) {

+				 for (Value value : record.getValues().values()) {

+					 row.addColumn(new Column(

+							 record.getEntityType().getName(),

+							 value.getName(), 

+							 Strings.emptyToNull(Objects.toString(value.extract())), 

+							 Strings.emptyToNull(value.getUnit())));

+				 }

+			 }

+			 rows.add(row);

+		 }

+		 

+		 ObjectMapper mapper = new ObjectMapper();

+		 mapper.enable(SerializationFeature.INDENT_OUTPUT);

+		 mapper.writeValue(System.out, rows);

+	}

+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.connector/build.gradle b/org.eclipse.mdm.connector/build.gradle
index 01dee57..072a834 100644
--- a/org.eclipse.mdm.connector/build.gradle
+++ b/org.eclipse.mdm.connector/build.gradle
@@ -13,5 +13,10 @@
 version = '1.0.0'
 
 dependencies {	
-	compile 'org.eclipse.mdm:org.eclipse.mdm.api.odsadapter:1.0.0'
+	compile 'org.eclipse.mdm:org.eclipse.mdm.api.base:1.0.0'
+	compile 'org.eclipse.mdm:org.eclipse.mdm.api.default:1.0.0'
+	compile project(':org.eclipse.mdm.property')
+	compile 'javax:javaee-api:7.0'
+
+	runtime 'org.eclipse.mdm:org.eclipse.mdm.api.odsadapter:1.0.0'
 }
diff --git a/org.eclipse.mdm.connector/src/main/configuration/service.xml b/org.eclipse.mdm.connector/src/main/configuration/service.xml
index 0b9fb48..2f97107 100644
--- a/org.eclipse.mdm.connector/src/main/configuration/service.xml
+++ b/org.eclipse.mdm.connector/src/main/configuration/service.xml
@@ -1,5 +1,14 @@
 <services>
-	<service nameServiceHost="YOUR_HOST1" nameServicePort="2809" nameServiceName="NameService" serviceName="YOUR_SERVICE1.ASAM-ODS"/>
-	<service nameServiceHost="YOUR_HOST2" nameServicePort="2809" nameServiceName="NameService" serviceName="YOUR_SERVICE2.ASAM-ODS"/>
-	<service nameServiceHost="YOUR_HOST3" nameServicePort="2809" nameServiceName="NameService" serviceName="YOUR_SERVICE3.ASAM-ODS"/>	
-</services>
\ No newline at end of file
+	<service entityManagerFactoryClass="org.eclipse.mdm.api.odsadapter.ODSEntityManagerFactory">
+		<param name="nameservice">corbaloc::1.2@YOUR_HOST1:2809/NameService</param>
+		<param name="servicename">YOUR_SERVICE1.ASAM-ODS</param>
+	</service>
+	<service entityManagerFactoryClass="org.eclipse.mdm.api.odsadapter.ODSEntityManagerFactory">
+		<param name="nameservice">corbaloc::1.2@YOUR_HOST2:2809/NameService</param>
+		<param name="servicename">YOUR_SERVICE2.ASAM-ODS</param>
+	</service>
+	<service entityManagerFactoryClass="org.eclipse.mdm.api.odsadapter.ODSEntityManagerFactory">
+		<param name="nameservice">corbaloc::1.2@YOUR_HOST3:2809/NameService</param>
+		<param name="servicename">YOUR_SERVICE3.ASAM-ODS</param>
+	</service>
+</services>
diff --git a/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/boundary/ConnectorService.java b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/boundary/ConnectorService.java
index b0e44cc..0367a99 100644
--- a/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/boundary/ConnectorService.java
+++ b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/boundary/ConnectorService.java
@@ -14,6 +14,7 @@
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -25,12 +26,13 @@
 import javax.inject.Inject;
 import javax.security.auth.spi.LoginModule;
 
+import org.eclipse.mdm.api.base.BaseEntityManager;
 import org.eclipse.mdm.api.base.ConnectionException;
 import org.eclipse.mdm.api.base.EntityManagerFactory;
+import org.eclipse.mdm.api.base.model.BaseEntityFactory;
 import org.eclipse.mdm.api.base.model.Environment;
 import org.eclipse.mdm.api.base.query.DataAccessException;
 import org.eclipse.mdm.api.dflt.EntityManager;
-import org.eclipse.mdm.api.odsadapter.ODSEntityManagerFactory;
 import org.eclipse.mdm.connector.control.ServiceConfigurationActivity;
 import org.eclipse.mdm.connector.entity.ServiceConfiguration;
 import org.eclipse.mdm.property.GlobalProperty;
@@ -41,6 +43,7 @@
  * ConnectorServcie Bean implementation to create and close connections
  * 
  * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ * @author Canoo Engineering (removal of hardcoded ODS dependencies)
  *
  */
 @Startup
@@ -49,10 +52,9 @@
 
 	private static final Logger LOG = LoggerFactory.getLogger(ConnectorService.class);
 
-	private final static String ARG_ODS_NAMESERVICE = "nameservice";
-	private final static String ARG_ODS_SERVICENAME = "servicename";
-	private final static String ARG_ODS_USER = "user";
-	private final static String ARG_ODS_PASSWORD = "password";
+	private static final String CONNECTION_PARAM_USER = "user";
+	private static final String CONNECTION_PARAM_PASSWORD = "password";
+	private static final String CONNECTION_PARAM_ELASTIC_SEARCH_URL = "elasticsearch.url";
 
 	@Resource
 	private SessionContext sessionContext;
@@ -61,11 +63,9 @@
 
 	@Inject
 	@GlobalProperty("elasticsearch.url")
-	String host;
+	private String elasticSearchURL;
 
-	private Map<Principal, List<EntityManager>> connectionMap = new HashMap<Principal, List<EntityManager>>();
-
-	private EntityManagerFactory<EntityManager> emf;
+	private Map<Principal, List<EntityManager>> connectionMap = new HashMap<>();
 
 	/**
 	 * returns all available {@link EntityManager}s
@@ -125,7 +125,7 @@
 	 */
 	public List<EntityManager> connect(String user, String password) {
 
-		List<EntityManager> emList = new ArrayList<EntityManager>();
+		List<EntityManager> emList = new ArrayList<>();
 
 		List<ServiceConfiguration> serviceConfigurations = serviceConfigurationActivity.readServiceConfigurations();
 
@@ -138,7 +138,7 @@
 
 	/**
 	 * registers all connections for a {@link Principal} at the
-	 * {@link ConnectorBean} This method is call from a {@link LoginModule} at
+	 * {@link ConnectorService} This method is call from a {@link LoginModule} at
 	 * login phase 2.
 	 *
 	 * @param principal
@@ -184,31 +184,38 @@
 
 		try {
 
-			Map<String, String> connectionParameters = new HashMap<String, String>();
-			connectionParameters.put(ARG_ODS_NAMESERVICE, source.getNameService());
-			connectionParameters.put(ARG_ODS_SERVICENAME, source.getServiceName());
-			connectionParameters.put(ARG_ODS_USER, user);
-			connectionParameters.put(ARG_ODS_PASSWORD, password);
+			@SuppressWarnings("rawtypes")
+			Class<? extends EntityManagerFactory> entityManagerFactoryClass = Thread.currentThread().getContextClassLoader().loadClass(source.getEntityManagerFactoryClass()).asSubclass(EntityManagerFactory.class);
+			EntityManagerFactory<?> emf = entityManagerFactoryClass.newInstance();
 
-			if (emf == null) {
-				emf = new ODSEntityManagerFactory(host);
+			Map<String, String> staticConnectionParameters = source.getConnectionParameters();
+			Map<String, String> dynamicConnectionParameters = new LinkedHashMap<>(staticConnectionParameters.size() + 3);
+			dynamicConnectionParameters.putAll(staticConnectionParameters);
+			dynamicConnectionParameters.put(CONNECTION_PARAM_USER, user);
+			dynamicConnectionParameters.put(CONNECTION_PARAM_PASSWORD, password);
+			if (elasticSearchURL != null && !elasticSearchURL.isEmpty()) {
+				dynamicConnectionParameters.put(CONNECTION_PARAM_ELASTIC_SEARCH_URL, elasticSearchURL);
 			}
 
-			emList.add(emf.connect(connectionParameters));
+			BaseEntityManager<? extends BaseEntityFactory> em = emf.connect(dynamicConnectionParameters);
+			// The cast below is unsafe, but cannot be avoided without changing the API of this class.
+			emList.add((EntityManager)em);
 
 		} catch (ConnectionException e) {
 			LOG.warn("unable to logon user with name '" + user + "' at data source '" + source.toString()
-					+ "' (reason: " + e.getMessage() + "'");
+			+ "' (reason: " + e.getMessage() + ")");
+		} catch (Exception e) {
+			LOG.error("failed to initialize entity manager using factory '" + source.getEntityManagerFactoryClass() + "' (reason: " + e + ")", e);
 		}
 	}
 
-	private void disconnectEntityManagers(List<EntityManager> emList) {
+	private static void disconnectEntityManagers(List<EntityManager> emList) {
 		for (EntityManager em : emList) {
 			disconnectEntityManager(em);
 		}
 	}
 
-	private void disconnectEntityManager(EntityManager em) {
+	private static void disconnectEntityManager(EntityManager em) {
 		try {
 			if (em != null) {
 				em.close();
diff --git a/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/control/ServiceConfigurationActivity.java b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/control/ServiceConfigurationActivity.java
index 5b440a5..ce25792 100644
--- a/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/control/ServiceConfigurationActivity.java
+++ b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/control/ServiceConfigurationActivity.java
@@ -14,151 +14,106 @@
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 import javax.ejb.Stateless;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
 
 import org.eclipse.mdm.connector.boundary.ConnectorServiceException;
 import org.eclipse.mdm.connector.entity.ServiceConfiguration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
 
 /**
  * ServiceConfigurationReader to read MDM Service configurations from a service.xml file
  * 
  * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ * @author Canoo Engineering AG (support for arbitrary entity manager factories and connection parameters)
  *
  */
 @Stateless
 public class ServiceConfigurationActivity {
-	
+
+	private static final String COMPONENT_CONFIG_ROOT_FOLDER = "org.eclipse.mdm.connector";
+	private static final String SERVICE_XML_FILE_NAME = "service.xml";
+
 	private static final String ROOT_ELEMENT_NAME = "services";
-	private static final String SERVICE_ELEMENT = "service";
-	private static final String SERVICE_ATTR_NS_HOST = "nameServiceHost";
-	private static final String SERVICE_ATTR_NS_PORT = "nameServicePort";
-	private static final String SERVICE_ATTR_NS_NAME = "nameServiceName";
-	private static final String SERVICE_ATTR_SERVICENAME = "serviceName";
-	
-	private final static String COMPONENT_CONFIG_ROOT_FOLDER = "org.eclipse.mdm.connector";
-	private final static String SERVICE_XML_FILE_NAME = "service.xml";		
-	
-	private static final Logger LOG = LoggerFactory.getLogger(ServiceConfigurationActivity.class); 
-	
+	private static final String SERVICE_ELEMENT_NAME = "service";
+	private static final String PARAM_ELEMENT_NAME = "param";
+	private static final String EMFACTORYCLASS_ATTRIBUTE_NAME = "entityManagerFactoryClass";
+	private static final String NAME_ATTRIBUTE_NAME = "name";
+
 	public List<ServiceConfiguration> readServiceConfigurations() {
-		
-		InputStream is = null; 
-		
-		try {
-			List<ServiceConfiguration> list = new ArrayList<>();
-			
-			File serviceXML = getServiceFile();
-			
-			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-	        DocumentBuilder db = dbf.newDocumentBuilder();
-	        
-	        is = new BufferedInputStream(new FileInputStream(serviceXML));
-	        Document doc = db.parse(is);
-	       
-	        Element root = doc.getDocumentElement();
-	        if(!root.getNodeName().equalsIgnoreCase(ROOT_ELEMENT_NAME)) {
-	        	String message = "unable to find root element with name '" + ROOT_ELEMENT_NAME + "'";
-	        	throw new ConnectorServiceException(message);
-	        }
-	                
-	        Element[] services = getChildElementsByName(root, SERVICE_ELEMENT, true);
-	        for(Element service : services) {
-	        	list.add(readServiceConfiguration(service));
-	        }   
-	        
-	        return list;	        
-		} catch(ParserConfigurationException | SAXException | IOException e) {
-			throw new ConnectorServiceException(e.getMessage(), e);
-		} finally {
-			closeInputStream(is);
+		File serviceXML = getServiceFile();
+
+		try (InputStream is = new BufferedInputStream(new FileInputStream(serviceXML))) {
+
+			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+			Document doc = db.parse(is);
+
+			Element root = doc.getDocumentElement();
+			if (!ROOT_ELEMENT_NAME.equals(root.getNodeName())) {
+				throw new ConnectorServiceException("unable to find root element with name '" + ROOT_ELEMENT_NAME + "'");
+			}
+
+			NodeList serviceElements = root.getElementsByTagName(SERVICE_ELEMENT_NAME);
+			List<ServiceConfiguration> parsedServiceElements = new ArrayList<>(serviceElements.getLength());
+			for (int i = 0, n = serviceElements.getLength(); i < n; i++) {
+				parsedServiceElements.add(parseServiceElement((Element)serviceElements.item(i)));
+			}
+
+			return parsedServiceElements;
+
+		} catch(ConnectorServiceException e) {
+			throw e;
+		} catch(Exception e) {
+			throw new ConnectorServiceException(e.toString(), e);
 		}
 	}
 
 
-	private ServiceConfiguration readServiceConfiguration(Element service) {
-		String nameServiceHost = readElementAttribute(SERVICE_ATTR_NS_HOST, "", true, service);
-		String nameServicePort = readElementAttribute(SERVICE_ATTR_NS_PORT, "", true, service);
-		String nameServiceName = readElementAttribute(SERVICE_ATTR_NS_NAME, "NameService", false, service);
-		String serviceName = readElementAttribute(SERVICE_ATTR_SERVICENAME, "", true, service);
-		String nameService = "corbaloc::1.2@" + nameServiceHost + ":" + nameServicePort + "/" + nameServiceName;
-		return new ServiceConfiguration(nameService, serviceName);
+	private static ServiceConfiguration parseServiceElement(Element serviceElement) {
+		String entityManagerFactoryClass = readElementAttribute(serviceElement, EMFACTORYCLASS_ATTRIBUTE_NAME);
+		NodeList paramElements = serviceElement.getElementsByTagName(PARAM_ELEMENT_NAME);
+		Map<String, String> connectionParameters = new LinkedHashMap<>(paramElements.getLength());
+		for (int i = 0, n = paramElements.getLength(); i < n; i++) {
+			Element paramElement = (Element)paramElements.item(i);
+			String paramName = readElementAttribute(paramElement, NAME_ATTRIBUTE_NAME);
+			String paramValue = paramElement.getTextContent();
+			if (paramValue != null && !(paramValue = paramValue.trim()).isEmpty()) {
+				connectionParameters.put(paramName, paramValue);
+			}
+		}
+		return new ServiceConfiguration(entityManagerFactoryClass, connectionParameters);
 	}
-	
-	
-	private Element[] getChildElementsByName(Element element, String name, boolean mandatory) {
-		
-		List<Element> elements = new ArrayList<Element>();
-		
-		NodeList childNodes = element.getChildNodes();
-		for (int i = 0; i < childNodes.getLength(); i++) {
-			Node node = childNodes.item(i);
-			if(node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equalsIgnoreCase(name)) {
-				elements.add((Element) node);
-			}					
-		}
-		
-		if(mandatory && elements.size() <= 0) {
-			String errorMessage = "mandatory element '" + name + "' not found!";
-			throw new ConnectorServiceException(errorMessage);
-		}
-		
-		return elements.toArray(new Element[elements.size()]);
-	}	
-	
-	
-	private String readElementAttribute(String attrName, String defaultValue, boolean mandatory, Element element) {	
+
+
+	private static String readElementAttribute(Element element, String attrName) {
 		String value = element.getAttribute(attrName);
-		if(value.trim().length() <= 0) {
-			if(mandatory) {
-				String elementName = element.getNodeName();
-				String errorMessage = "mandatory attribute '" + attrName + "' at element '" + elementName + "' is missing!";				
-				throw new ConnectorServiceException(errorMessage);
-			}			
-			value = defaultValue;
+		if (value.trim().isEmpty()) {
+			throw new ConnectorServiceException("mandatory attribute '" + attrName + "' of element '" + element.getNodeName() + "' is missing!");
 		}
 		return value;
 	}
-	
-	
-	private File getServiceFile() {
+
+
+	private static File getServiceFile() {
 		File file = new File(COMPONENT_CONFIG_ROOT_FOLDER);
-		if(!file.exists() || !file.isDirectory()) {
-			String errorMessage = "mandatory configuration folder '" + file.getAbsolutePath() + "' does not exist!";
-			throw new ConnectorServiceException(errorMessage);
+		if (!file.exists() || !file.isDirectory()) {
+			throw new ConnectorServiceException("mandatory configuration folder '" + file.getAbsolutePath() + "' does not exist!");
 		}
 		File serviceXML = new File(file, SERVICE_XML_FILE_NAME);
-		if(!file.exists()) {
-			String errorMessage = "mandatory service configuration file at '" + serviceXML.getAbsolutePath() 
-			+ "' does not exist!";
-			throw new ConnectorServiceException(errorMessage);
+		if (!file.exists()) {
+			throw new ConnectorServiceException("mandatory service configuration file at '" + serviceXML.getAbsolutePath() + "' does not exist!");
 		}
 		return serviceXML;
 	}
-	
-	
-	
-	private void closeInputStream(InputStream is) {
-		try {
-			if(is != null) {
-				is.close();
-			}
-		} catch(IOException e) {
-			LOG.error(e.getMessage());
-		}
-	}
+
+
 }
diff --git a/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/entity/ServiceConfiguration.java b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/entity/ServiceConfiguration.java
index 76af6a6..295d6e6 100644
--- a/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/entity/ServiceConfiguration.java
+++ b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/entity/ServiceConfiguration.java
@@ -11,54 +11,77 @@
 
 package org.eclipse.mdm.connector.entity;
 
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+
 /**
  * ServiceConfiguration
  * 
  * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ * @author Canoo Engineering AG (support for arbitrary entity manager factories and connection parameters)
  *
  */
 public class ServiceConfiguration {
 	
-	private final String nameService;
-	private final String serviceName;
+	/**
+	 * The fully qualified class name of the entity manager factory
+	 * for this backend, never null.
+	 */
+	private final String entityManagerFactoryClass;
+
+	/**
+	 * An unmodifiable map holding the connection parameters
+	 * for this backend; never null, but possibly empty.
+	 */
+	private final Map<String, String> connectionParameters;
 
 	
 	/**
-	 * Constructor
+	 * Constructs a new instance with the specified properties.
+	 * The specified parameter map is copied by this constructor
+	 * and no reference to the original is retained.
 	 * 
-	 * @param nameService name service
-	 * @param serviceName service name
+	 * @param entityManagerFactoryClass
+	 *	  the fully qualified class name of the entity manager factory
+	 *	  for this backend, must not be null
+	 * @param connectionParameters
+	 *	  a map holding the connection parameters for this backend,
+	 *	  or null to use an empty map instead
 	 */
-	public ServiceConfiguration(String nameService, String serviceName) {
-		this.nameService = nameService;
-		this.serviceName = serviceName;	
+	public ServiceConfiguration(String entityManagerFactoryClass, Map<String, String> connectionParameters) {
+		this.entityManagerFactoryClass = Objects.requireNonNull(entityManagerFactoryClass, "Null \"entityManagerFactoryClass\" argument passed to ServiceConfiguration constructor");
+		this.connectionParameters = (connectionParameters == null ? Collections.emptyMap() : Collections.unmodifiableMap(new LinkedHashMap<>(connectionParameters)));	
 	}
 	
-	
-	
+	/**
+	 * Returns the fully qualified class name of the entity manager factory
+	 * for this backend. The result is never null.
+	 * 
+	 * @return
+	 *	  the entity manager factory class name passed to the constructor,
+	 *	  never null
+	 */
+	public String getEntityManagerFactoryClass() {
+		return entityManagerFactoryClass;
+	}
+
+	/**
+	 * Returns an unmodifiable map holding the connection parameters
+	 * for this backend. The result is never null, but may be empty.
+	 * 
+	 * @return
+	 *	  an unmodifiable copy of the connection parameter map
+	 *	  passed to the constructor; never null, but possibly empty
+	 */
+	public Map<String, String> getConnectionParameters() {
+		return connectionParameters;
+	}
+
 	@Override
 	public String toString() {
-		return this.nameService + "#" + serviceName;
-	}
-	
-	
-	
-	/**
-	 * returns the name service of this configuration 
-	 * @return the name service of this configuration
-	 */
-	public String getNameService() {
-		return nameService;
-	}
-	
-	
-	
-	/**
-	 * returns the service name of this configuration
-	 * @return the service name of this configuration
-	 */
-	public String getServiceName() {
-		return serviceName;
+		return this.entityManagerFactoryClass + "#" + connectionParameters;
 	}
 
 }
diff --git a/org.eclipse.mdm.connector/src/test/java/org/eclipse/mdm/connector/ConnectorServiceTest.java b/org.eclipse.mdm.connector/src/test/java/org/eclipse/mdm/connector/ConnectorServiceTest.java
index f78b487..35b48cd 100644
--- a/org.eclipse.mdm.connector/src/test/java/org/eclipse/mdm/connector/ConnectorServiceTest.java
+++ b/org.eclipse.mdm.connector/src/test/java/org/eclipse/mdm/connector/ConnectorServiceTest.java
@@ -11,29 +11,33 @@
 
 package org.eclipse.mdm.connector;
 
+import static org.junit.Assert.*;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.when;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.security.Principal;
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import javax.ejb.SessionContext;
 
+import org.eclipse.mdm.api.base.ConnectionException;
 import org.eclipse.mdm.api.base.EntityManagerFactory;
 import org.eclipse.mdm.api.base.model.Core;
 import org.eclipse.mdm.api.base.model.Entity;
 import org.eclipse.mdm.api.base.model.Environment;
 import org.eclipse.mdm.api.base.model.Value;
 import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.DataAccessException;
 import org.eclipse.mdm.api.dflt.EntityManager;
 import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.eclipse.mdm.connector.boundary.ConnectorServiceException;
 import org.eclipse.mdm.connector.control.ServiceConfigurationActivity;
 import org.eclipse.mdm.connector.entity.ServiceConfiguration;
 import org.junit.Test;
@@ -42,150 +46,190 @@
 /**
  * JUNIT Test for {@link ConnectorService}
  * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ * @author Canoo Engineering (more tests)
  *
  */
+@SuppressWarnings("javadoc")
 public class ConnectorServiceTest {
 
-	@Test
-	public void testGetEntityManagers() throws Exception {
-		ConnectorService connectorService = createMockedconnectorService();
-		List<EntityManager> emList = connectorService.getEntityManagers();
+	private final Principal testUser = new SimplePrincipal("testUser");
+	private final Principal differentUser = new SimplePrincipal("differentUser");
+	private final String testSourceName = "testSource";
+	private final String differentSourceName = "differentSource";
+	private final EntityManager testEntityManager = createEntityManager(testSourceName);
 
-		assertNotNull("entity manager for uri should not be null", emList);
-		assertNotEquals("size of entity manager list should be grater then 0", 0, emList.size());
 
+	@Test(expected=ConnectorServiceException.class)
+	public void testGetEntityManagers_noConnectionsYet() throws Exception {
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		connectorService.getEntityManagers();
+	}
+
+
+	@Test(expected=ConnectorServiceException.class)
+	public void testGetEntityManagers_connectionForDifferentUser() throws Exception {
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		connectorService.registerConnections(differentUser, Collections.singletonList(testEntityManager));
+		connectorService.getEntityManagers();
 	}
 
 
 	@Test
-	public void testGetEntityManagerByName() throws Exception {
+	public void testGetEntityManagers_happyFlow() throws Exception {
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		connectorService.registerConnections(testUser, Collections.singletonList(testEntityManager));
+		assertEquals(Collections.singletonList(testEntityManager), connectorService.getEntityManagers());
+	}
 
-		ConnectorService connectorService = createMockedconnectorService();
-		EntityManager em = connectorService.getEntityManagerByName("MDMTest");
-		assertNotNull("entity manager for uri should not be null", em);
+
+	@Test(expected=ConnectorServiceException.class)
+	public void testGetEntityManagerByName_noConnectionsYet() throws Exception {
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		connectorService.getEntityManagerByName(testSourceName);
+	}
+
+
+	@Test(expected=ConnectorServiceException.class)
+	public void testGetEntityManagerByName_connectionForDifferentUser() throws Exception {
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		connectorService.registerConnections(differentUser, Collections.singletonList(testEntityManager));
+		connectorService.getEntityManagerByName(testSourceName);
+	}
+
+
+	@Test(expected=ConnectorServiceException.class)
+	public void testGetEntityManagerByName_differentSourceName() throws Exception {
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		connectorService.registerConnections(testUser, Collections.singletonList(testEntityManager));
+		connectorService.getEntityManagerByName(differentSourceName);
+	}
+
+
+	@Test
+	public void testGetEntityManagerByName_happyFlow() throws Exception {
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		connectorService.registerConnections(testUser, Collections.singletonList(testEntityManager));
+		assertSame(testEntityManager, connectorService.getEntityManagerByName(testSourceName));
 	}
 
 
 	@Test
 	public void testConnect() throws Exception {
-
-		Principal principal = Mockito.mock(Principal.class);
-		when(principal.toString()).thenReturn("testuser");
-
-		ConnectorService connectorService = createMockedconnectorService();
-		List<EntityManager> emList = connectorService.connect("user", "password");
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		List<EntityManager> emList = connectorService.connect("someUser", "somePassword");
 
 		assertNotNull("EntityManager list should not be null", emList);
-		assertNotEquals("EntityManager list size be grather then 0", 0, emList.size());
+		assertNotEquals("EntityManager list size should be greater than 0", 0, emList.size());
 	}
 
 
-	@Test
-	public void testRegisterConnections() throws Exception {
-
-		Principal principal = Mockito.mock(Principal.class);
-		when(principal.toString()).thenReturn("testuser");
-
-		ConnectorService connectorService = createMockedconnectorService();
-		List<EntityManager> emList = connectorService.connect("user", "password");
-
-		connectorService.registerConnections(principal, emList);
+	@Test(expected=ConnectorServiceException.class)
+	public void testRegisterConnections_emptyList() throws Exception {
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		connectorService.registerConnections(testUser, Collections.emptyList());
 	}
 
 
 	@Test
 	public void testDisconnect() throws Exception {
-
-		Principal principal = Mockito.mock(Principal.class);
-		when(principal.toString()).thenReturn("testuser");
-
-		ConnectorService connectorService = createMockedconnectorService();
-		connectorService.disconnect(principal);
+		ConnectorService connectorService = createMockedConnectorService(testUser);
+		connectorService.disconnect(testUser);
 
 	}
 
 
+	private static final class SimplePrincipal implements Principal {
+		private final String name;
 
-	@SuppressWarnings("unchecked")
-	private ConnectorService createMockedconnectorService() throws Exception {
+		SimplePrincipal(String name) {
+			this.name = Objects.requireNonNull(name);
+		}
 
-		Principal principal = Mockito.mock(Principal.class);
-		when(principal.toString()).thenReturn("testuser");
+		@Override
+		public String getName() {
+			return name;
+		}
 
-		SessionContext sessionContext = Mockito.mock(SessionContext.class);
-		when(sessionContext.getCallerPrincipal()).thenReturn(principal);
+		@Override
+		public boolean equals(Object obj) {
+			return (obj instanceof SimplePrincipal && ((SimplePrincipal)obj).name.equals(name));
+		}
 
-		Environment env = createEntityMock(Environment.class, "MDMTest", "MDMTest", 1L);
+		@Override
+		public int hashCode() {
+			return name.hashCode();
+		}
 
-		EntityManager em = Mockito.mock(EntityManager.class);
-		when(em.loadEnvironment()).thenReturn(env);
+		@Override
+		public String toString() {
+			return name;
+		}
 
-		List<EntityManager> emList = new ArrayList<EntityManager>();
-		emList.add(em);
+	}
 
-		EntityManagerFactory<EntityManager> emf = Mockito.mock(EntityManagerFactory.class);
-		when(emf.connect(anyObject())).thenReturn(em);
+	private static ConnectorService createMockedConnectorService(Principal user) throws Exception {
 
-		Map<Principal, List<EntityManager>> map = new HashMap<>();
-		map.put(principal, emList);
+		SessionContext sessionContextMock = Mockito.mock(SessionContext.class);
+		when(sessionContextMock.getCallerPrincipal()).thenReturn(user);
 
-		List<ServiceConfiguration> scList = new ArrayList<>();
-		scList.add(new ServiceConfiguration("nameService", "serviceName"));
-
+		ServiceConfiguration serviceConfiguration = new ServiceConfiguration(TestEntityManagerFactory.class.getName(), Collections.emptyMap());
 		ServiceConfigurationActivity scReaderMock = Mockito.mock(ServiceConfigurationActivity.class);
-		when(scReaderMock.readServiceConfigurations()).thenReturn(scList);
-
+		when(scReaderMock.readServiceConfigurations()).thenReturn(Collections.singletonList(serviceConfiguration));
 
 		ConnectorService connectorService = new ConnectorService();
 
 		Field scField = connectorService.getClass().getDeclaredField("sessionContext");
 		scField.setAccessible(true);
-		scField.set(connectorService, sessionContext);
-		scField.setAccessible(false);
-
-		Field mapField = connectorService.getClass().getDeclaredField("connectionMap");
-		mapField.setAccessible(true);
-		mapField.set(connectorService, map);
-		mapField.setAccessible(false);
-
-		Field emfField = connectorService.getClass().getDeclaredField("emf");
-		emfField.setAccessible(true);
-		emfField.set(connectorService, emf);
-		emfField.setAccessible(false);
+		scField.set(connectorService, sessionContextMock);
 
 		Field scrField = connectorService.getClass().getDeclaredField("serviceConfigurationActivity");
 		scrField.setAccessible(true);
 		scrField.set(connectorService, scReaderMock);
-		scrField.setAccessible(false);
 
 		return connectorService;
 	}
 
 
-	private <T extends Entity> T createEntityMock(Class<T> type, String name,
-			String sourceName, Long id) throws Exception {
+	public static final class TestEntityManagerFactory implements EntityManagerFactory<EntityManager> {
 
-		boolean accessible = false;
-		Constructor<T> constructor = null;
+		@Override
+		public EntityManager connect(Map<String, String> connectionParameters) throws ConnectionException {
+			return createEntityManager("someSource");
+		}
+
+	}
+
+
+	private static EntityManager createEntityManager(String sourceName) {
+		Environment env = createEntityMock(Environment.class, "MDMTest", sourceName, 1L);
+
+		EntityManager em = Mockito.mock(EntityManager.class);
+		try {
+			when(em.loadEnvironment()).thenReturn(env);
+		} catch (@SuppressWarnings("unused") DataAccessException e) { 
+			// ignore - cannot happen
+		}
+
+		return em;
+	}
+
+
+	private static <T extends Entity> T createEntityMock(Class<T> type, String name, String sourceName, long id) {
+
+		Map<String, Value> entityAttributes = new HashMap<>();
+		entityAttributes.put("Name", ValueType.STRING.create("Name", name));
+
+		Core core = Mockito.mock(Core.class);
+		when(core.getSourceName()).thenReturn(sourceName);
+		when(core.getValues()).thenReturn(entityAttributes);
+		when(core.getID()).thenReturn(id);
 
 		try {
-			HashMap<String, Value> map = new HashMap<String, Value>();
-			map.put("Name", ValueType.STRING.create("Name", name));
-
-			Core core = Mockito.mock(Core.class);
-			when(core.getSourceName()).thenReturn(sourceName);
-			when(core.getValues()).thenReturn(map);
-			when(core.getID()).thenReturn(id);
-
-			constructor = type.getDeclaredConstructor(Core.class);
-			accessible = constructor.isAccessible();
+			Constructor<T> constructor = type.getDeclaredConstructor(Core.class);
 			constructor.setAccessible(true);
-			T instance = constructor.newInstance(core);
-			return instance;
-		} finally {
-			if(constructor != null) {
-				constructor.setAccessible(accessible);
-			}
+			return constructor.newInstance(core);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
 		}
 	}
 
diff --git a/org.eclipse.mdm.freetextindexer/build.gradle b/org.eclipse.mdm.freetextindexer/build.gradle
index 91ea7b7..1dcb454 100644
--- a/org.eclipse.mdm.freetextindexer/build.gradle
+++ b/org.eclipse.mdm.freetextindexer/build.gradle
@@ -30,8 +30,8 @@
 	compile 'org.eclipse.mdm:org.eclipse.mdm.api.base:1.0.0'
 	compile 'org.eclipse.mdm:org.eclipse.mdm.api.odsadapter:1.0.0'
 	compile 'org.eclipse.mdm:org.eclipse.mdm.api.default:1.0.0'
-	compile 'org.eclipse.mdm:org.eclipse.mdm.connector:1.0.0'
-	compile 'org.eclipse.mdm:org.eclipse.mdm.property:1.0.0'
+	compile project(':org.eclipse.mdm.connector')
+	compile project(':org.eclipse.mdm.property')
 	
 	providedCompile 'javax:javaee-api:7.0'
 
diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/MdmApiBoundary.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/MdmApiBoundary.java
index e916643..6105db7 100644
--- a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/MdmApiBoundary.java
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/MdmApiBoundary.java
@@ -137,8 +137,8 @@
 				List<EntityManager> connectionList = service.connect(freetextUser, freetextPw);

 				if (connectionList.isEmpty()) {

 					String error = String.format(

-							"Cannot connect to MDM from Freetextindexer. Seems like the technical user/password is not correct (%s/%s)",

-							freetextUser, freetextPw);

+							"Cannot connect to MDM from Freetextindexer. Seems like the technical user/password is not correct (User: %s)",

+							freetextUser);

 					throw new IllegalArgumentException(error);

 				}

 

diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityResponse.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityResponse.java
index 814b9c3..0812deb 100644
--- a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityResponse.java
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityResponse.java
@@ -85,7 +85,7 @@
 					MDMEntity compEntity = toTransferable(comp);
 					entity.components.add(compEntity);
 					for (Entry<String, Value> entry : comp.getValues().entrySet()) {
-						compEntity.attributes.put(entry.getKey(), entry.getValue().toString());
+						compEntity.attributes.put(entry.getKey(), entry.getValue().extract().toString());
 					}
 				}
 			}
diff --git a/org.eclipse.mdm.preferences/build.gradle b/org.eclipse.mdm.preferences/build.gradle
new file mode 100644
index 0000000..1980c2f
--- /dev/null
+++ b/org.eclipse.mdm.preferences/build.gradle
@@ -0,0 +1,74 @@
+/*******************************************************************************
+  * Copyright (c) 2017 Peak Solution GmbH
+  * All rights reserved. This program and the accompanying materials
+  * are made available under the terms of the Eclipse Public License v1.0
+  * which accompanies this distribution, and is available at
+  * http://www.eclipse.org/legal/epl-v10.html
+  *
+  * Contributors:
+  * Matthias Koller - initial implementation
+  *******************************************************************************/
+
+description = 'MDM preference service'
+version = '1.0.0'
+
+apply plugin: 'java'
+apply plugin: 'maven'
+apply plugin: 'eclipse'
+apply plugin: 'jpa-schema-generate'
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+  
+repositories {
+  mavenLocal()
+  jcenter()
+  mavenCentral()
+}
+
+dependencies {
+  runtime  'org.glassfish.jersey.containers:jersey-container-servlet:2.15'
+  runtime  'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.8.1'
+  runtime  'javax:javaee-api:7.0'
+  
+  compile project(':org.eclipse.mdm.connector')
+  compile project(':org.eclipse.mdm.property')
+
+  testCompile  'org.eclipse.persistence:eclipselink:2.6.4'
+  testCompile 'junit:junit:4.12'
+  testCompile 'org.assertj:assertj-core:3.6.1'
+  testRuntime 'org.apache.derby:derby:10.13.1.1'
+}
+
+sourceSets {
+    main {
+      // set output to same directories
+      // jpa implementations always scan classes using classpath that found persistence.xml
+        output.resourcesDir = output.classesDir
+    }
+}
+
+generateSchema {
+  vendor = "eclipselink"
+  packageToScan = ["org.eclipse.mdm.preferences"]
+  scriptAction = "drop-and-create"
+  targets {
+    postgres {
+      databaseProductName = "PostgreSQL"
+      databaseMajorVersion = 9
+      databaseMinorVersion = 0
+    }
+    derby {
+      databaseProductName = "Apache Derby"
+    }
+  }
+}
+
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+    dependencies {
+        classpath 'io.github.divinespear:jpa-schema-gradle-plugin:+'
+    }
+}
diff --git a/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/boundary/PreferenceResource.java b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/boundary/PreferenceResource.java
new file mode 100644
index 0000000..94e5b82
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/boundary/PreferenceResource.java
@@ -0,0 +1,139 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Johannes Stamm - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.preferences.boundary;

+

+import java.util.List;

+

+import javax.ejb.EJB;

+import javax.ws.rs.Consumes;

+import javax.ws.rs.DELETE;

+import javax.ws.rs.GET;

+import javax.ws.rs.PUT;

+import javax.ws.rs.Path;

+import javax.ws.rs.PathParam;

+import javax.ws.rs.Produces;

+import javax.ws.rs.QueryParam;

+import javax.ws.rs.WebApplicationException;

+import javax.ws.rs.core.GenericEntity;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+import javax.ws.rs.core.Response.Status;

+

+import org.eclipse.mdm.preferences.controller.PreferenceService;

+import org.eclipse.mdm.preferences.entity.PreferenceMessage;

+import org.eclipse.mdm.preferences.entity.PreferenceList;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+/**

+ * 

+ * @author Johannes Stamm, Peak Solution GmbH

+ *

+ */

+@Path("/preferences")

+public class PreferenceResource {

+	

+	private static final Logger LOG = LoggerFactory.getLogger(PreferenceResource.class); 

+	

+	@EJB

+	private PreferenceService preferenceService;

+	

+	/**

+	 * delegates the request to the {@link PreferenceService}

+	 * 

+	 * @param scope filter by scope, empty loads all

+	 * @param key filter by key, empty loads all

+	 * @return the result of the delegated request as {@link Response}

+	 */

+	@GET

+	@Produces(MediaType.APPLICATION_JSON)

+	public Response getPreference(@QueryParam("scope") String scope, @QueryParam("key") String key) {

+		

+		try {			

+			List<PreferenceMessage> config = this.preferenceService.getPreferences(scope, key);

+			return toResponse(new PreferenceList(config), Status.OK);

+		

+		} catch(RuntimeException e) {

+			LOG.error(e.getMessage(), e);

+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);

+		}

+	}

+

+	/**

+	 * delegates the request to the {@link PreferenceService}

+	 * 

+	 * @param source filter by source

+	 * @param key filter by key, empty loads all

+	 * @return the result of the delegated request as {@link Response}

+	 */

+	@GET

+	@Path("/source")

+	@Produces(MediaType.APPLICATION_JSON)

+	public Response getPreferenceBySource(@QueryParam("source") String source, @QueryParam("key") String key) {

+		

+		try {			

+			List<PreferenceMessage> config = this.preferenceService.getPreferencesBySource(source, key);

+			return toResponse(new PreferenceList(config), Status.OK);

+		

+		} catch(RuntimeException e) {

+			LOG.error(e.getMessage(), e);

+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);

+		}

+	}

+	

+	/**

+	 * delegates the request to the {@link PreferenceService}

+	 * 

+	 * @param preference Configuration to save

+	 * @return the result of the delegated request as {@link Response}

+	 */

+	@PUT

+	@Consumes(MediaType.APPLICATION_JSON)

+	public Response setPreference(PreferenceMessage preference) {

+		

+		try {

+			return toResponse(this.preferenceService.save(preference), Status.CREATED);

+		} catch(RuntimeException e) {

+			LOG.error(e.getMessage(), e);

+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);

+		}

+	}

+	

+	/**

+	 * delegates the request to the {@link PreferenceService}

+	 * 

+	 * @param preference Configuration to delete

+	 * @return the result of the delegated request as {@link Response}

+	 */

+	@DELETE

+	@Path("/{ID}")

+	public Response deletePreference(@PathParam("ID") Long id){

+		

+		try {

+			return toResponse(this.preferenceService.deletePreference(id), Status.OK);

+		} catch(RuntimeException e) {

+			LOG.error(e.getMessage(), e);

+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);

+		}

+	}

+	

+	/**

+	 * converts the given object to a {@link Response} with the given {@link Status}

+	 *

+	 * @param responseEntry object to convert

+	 * @param status {@link Status} of the {@link Response}

+	 * @return the created {@link Response}

+	 */

+	private Response toResponse(Object response, Status status) {

+		GenericEntity<Object> genEntity = new GenericEntity<>(response, response.getClass());

+		return Response.status(status).entity(genEntity).type(MediaType.APPLICATION_JSON).build();

+	}

+}

diff --git a/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/controller/PreferenceService.java b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/controller/PreferenceService.java
new file mode 100644
index 0000000..d36bad2
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/controller/PreferenceService.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+  * Copyright (c) 2017 Peak Solution GmbH
+  * All rights reserved. This program and the accompanying materials
+  * are made available under the terms of the Eclipse Public License v1.0
+  * which accompanies this distribution, and is available at
+  * http://www.eclipse.org/legal/epl-v10.html
+  *
+  * Contributors:
+  * Johannes Stamm - initial implementation
+  *******************************************************************************/
+package org.eclipse.mdm.preferences.controller;
+
+import java.security.Principal;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.annotation.Resource;
+import javax.ejb.SessionContext;
+import javax.ejb.Stateless;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import javax.persistence.TypedQuery;
+
+import org.eclipse.mdm.preferences.entity.Preference;
+import org.eclipse.mdm.preferences.entity.PreferenceMessage;
+import org.eclipse.mdm.preferences.entity.PreferenceMessage.Scope;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+
+/**
+ * 
+ * @author Johannes Stamm, Peak Solution GmbH
+ *
+ */
+@Stateless
+public class PreferenceService
+{
+	private static final Logger LOG = LoggerFactory.getLogger(PreferenceService.class); 
+	
+	@PersistenceContext(unitName="openMDM")
+	private EntityManager em;
+
+	@Resource
+	private SessionContext sessionContext;
+	
+	public PreferenceService() {
+		// public no-arg constructor
+	}
+	
+	/**
+	 * Constructor for unit tests
+	 * @param em EntityManager to use
+	 * @param sessionContext sessionManager to use
+	 */
+	PreferenceService(EntityManager em, SessionContext sessionContext) {
+		this.em = em;
+		this.sessionContext = sessionContext;
+	}
+	
+	public List<PreferenceMessage> getPreferences(String scope, String key) {
+
+		TypedQuery<Preference> query;
+		if (scope == null || scope.trim().isEmpty()) {
+			query = em.createQuery("select p from Preference p where (p.user is null or p.user = :user) and LOWER(p.key) like :key", Preference.class)
+				.setParameter("user", sessionContext.getCallerPrincipal().getName())
+				.setParameter("key", key.toLowerCase() + "%");
+		} else {
+			query = em.createQuery(buildQuery(scope, key), Preference.class);
+			
+			if (key != null && !key.trim().isEmpty()) {
+				query.setParameter("key", key.toLowerCase() + "%");
+			}
+		}
+		
+		return query.getResultList()
+				.stream()
+				.map(this::convert)
+				.collect(Collectors.toList());
+	}
+	
+	public List<PreferenceMessage> getPreferencesBySource(String source, String key) {
+		TypedQuery<Preference> query;
+		
+		if (key == null || key.trim().isEmpty()) {
+			query = em.createQuery("select p from Preference p where p.source = :source", Preference.class)
+				.setParameter("source", Strings.emptyToNull(source));
+				
+		} else {
+			query = em.createQuery("select p from Preference p where p.source = :source and p.key = :key", Preference.class)
+				.setParameter("source", Strings.emptyToNull(source))
+				.setParameter("key", Strings.emptyToNull(key));
+		}
+		return query.getResultList()
+				.stream()
+				.map(this::convert)
+				.collect(Collectors.toList());
+	}
+	
+	public PreferenceMessage save(PreferenceMessage preference) {
+		Principal principal = sessionContext.getCallerPrincipal();
+		List<Preference> existingPrefs = getExistingPreference(preference, principal);
+		
+		Preference pe;
+		
+		if (existingPrefs.isEmpty()) {
+			pe = convert(preference);
+		} else {
+			if (existingPrefs.size() > 1) {
+				LOG.warn("Found multiple entries for preference with scope={}, source={}, user={}, key={} where one entry was expected!", 
+						preference.getScope(), preference.getSource(), preference.getUser(), preference.getKey() );
+			}
+			pe = existingPrefs.get(0);
+			pe.setValue(preference.getValue());
+		}
+		em.persist(pe);
+		em.flush();
+		return convert(pe);
+	}
+
+	public PreferenceMessage deletePreference(Long id){
+		Preference preference = em.find(Preference.class, id);
+		em.remove(preference);
+		em.flush();
+		return convert(preference);
+	}
+
+	
+	private PreferenceMessage convert(Preference pe)
+	{
+		PreferenceMessage p = new PreferenceMessage();
+		p.setKey(pe.getKey());
+		p.setValue(pe.getValue());
+		p.setId(pe.getId());
+		
+		if (pe.getUser() == null && pe.getSource() == null) {
+			p.setSource(pe.getSource());
+			p.setScope(Scope.SYSTEM);
+		} else if (pe.getUser() != null) {
+			p.setUser(pe.getUser());
+			p.setScope(Scope.USER);
+		} else if (pe.getSource() != null) {
+			p.setSource(pe.getSource());
+			p.setScope(Scope.SOURCE);
+		} 
+		
+		return p;
+	}
+	
+	private Preference convert(PreferenceMessage p)
+	{
+		Principal principal = sessionContext.getCallerPrincipal();
+
+		Preference pe = new Preference();
+		pe.setKey(p.getKey());
+		pe.setValue(p.getValue());
+		pe.setId(p.getId());
+		
+		switch (p.getScope()) {
+			case SOURCE:
+				pe.setSource(p.getSource());
+				break;
+			case USER:
+				pe.setUser(principal.getName());
+				break;
+			case SYSTEM:
+			default:
+				break;
+		}
+		return pe;
+	}
+	
+	private String buildQuery(String scope, String key){
+		String query = "select p from Preference p";
+		String whereOrAnd = " where";
+		if(scope != null && scope.trim().length() > 0) {
+			switch (scope.toLowerCase()) {
+				case "system":
+					query = query.concat(whereOrAnd).concat(" p.source is null and p.user is null");
+					break;
+				case "source":
+					query = query.concat(whereOrAnd).concat(" p.source is not null and p.user is null");
+					break;
+				case "user":
+					query = query.concat(whereOrAnd).concat(" p.source is null and p.user is not null");
+					break;
+				default:
+			}
+			whereOrAnd = " and";
+		}
+		if(key != null && key.trim().length() > 0) {
+			query = query.concat(whereOrAnd).concat(" LOWER(p.key) LIKE :key");
+		}
+		return query;
+	}
+
+	private List<Preference> getExistingPreference(PreferenceMessage preference, Principal principal) {
+		Preconditions.checkNotNull(preference.getScope(), "Scope cannot be null!");
+		
+		if (preference.getId() == null) {
+			switch (preference.getScope())
+			{
+			case USER:
+				return em.createQuery(
+						"select p from Preference p where p.source is null and p.user = :user and p.key = :key", Preference.class)
+						.setParameter("user", Strings.emptyToNull(principal.getName()))
+						.setParameter("key", preference.getKey())
+						.getResultList();			
+			case SOURCE:
+				return em.createQuery(
+						"select p from Preference p where p.source = :source and p.user is null and p.key = :key", Preference.class)
+						.setParameter("source", Strings.emptyToNull(preference.getSource()))
+						.setParameter("key", preference.getKey())
+						.getResultList();			
+			case SYSTEM:
+				return em.createQuery(
+						"select p from Preference p where p.source is null and p.user is null and p.key = :key", Preference.class)
+						.setParameter("key", preference.getKey())
+						.getResultList();	
+			default:
+				throw new IllegalArgumentException("Unknown Scope!");
+			}
+		} else {
+			return Arrays.asList(em.find(Preference.class, preference.getId()));
+		}
+	}
+}
diff --git a/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/Preference.java b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/Preference.java
new file mode 100644
index 0000000..f9a6c48
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/Preference.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+  * Copyright (c) 2017 Peak Solution GmbH
+  * All rights reserved. This program and the accompanying materials
+  * are made available under the terms of the Eclipse Public License v1.0
+  * which accompanies this distribution, and is available at
+  * http://www.eclipse.org/legal/epl-v10.html
+  *
+  * Contributors:
+  * Johannes Stamm - initial implementation
+  *******************************************************************************/
+package org.eclipse.mdm.preferences.entity;
+
+import java.util.Objects;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * 
+ * @author Johannes Stamm, Peak Solution GmbH
+ *
+ */
+@Entity
+@Table(uniqueConstraints={@UniqueConstraint(columnNames={"source", "username", "keyCol"})})
+public class Preference {
+	
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	private Long id;
+
+	@Column
+	private String source;
+	
+	@Column(name="username")
+	private String user;
+	
+	@Column(name="keyCol")
+	private String key;
+	
+	@Column(name="valueCol", nullable=false)
+	@Lob
+	private String value;
+
+	public Preference()
+	{
+		super();
+	}
+
+	public Preference(String source, String user, String key, String value)
+	{
+		super();
+		this.source = source;
+		this.user = user;
+		this.key = key;
+		this.value = value;
+	}
+
+	public Long getId()
+	{
+		return id;
+	}
+
+	public void setId(Long id)
+	{
+		this.id = id;
+	}
+
+	public String getSource()
+	{
+		return source;
+	}
+
+	public void setSource(String source)
+	{
+		this.source = source;
+	}
+
+	public String getUser()
+	{
+		return user;
+	}
+
+	public void setUser(String user)
+	{
+		this.user = user;
+	}
+
+	public String getKey()
+	{
+		return key;
+	}
+
+	public void setKey(String key)
+	{
+		this.key = key;
+	}
+
+	public String getValue()
+	{
+		return value;
+	}
+
+	public void setValue(String value)
+	{
+		this.value = value;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+
+		if (obj == null) {
+			return false;
+		}
+		if (getClass() != obj.getClass()) {
+			return false;
+		}
+
+		final Preference other = (Preference) obj;
+		return new EqualsBuilder()
+				.append(this.id, other.id)
+				.append(this.source, other.source)
+				.append(this.user, other.user)
+				.append(this.key, other.key)
+				.append(this.value, other.value)
+				.isEquals();
+	}
+	
+	@Override
+	public int hashCode() {
+		return Objects.hash(id, source, user, key, value);
+	}
+	
+	@Override
+	public String toString() {
+		return MoreObjects.toStringHelper(Preference.class)
+				.add("id", id)
+				.add("source", source)
+				.add("user", user)
+				.add("key", key)
+				.add("value", value)
+				.toString();
+	}
+	
+}
diff --git a/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/PreferenceList.java b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/PreferenceList.java
new file mode 100644
index 0000000..0b401d0
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/PreferenceList.java
@@ -0,0 +1,67 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Johannes Stamm - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.preferences.entity;

+

+import java.util.List;

+import java.util.Objects;

+

+import javax.xml.bind.annotation.XmlAccessType;

+import javax.xml.bind.annotation.XmlAccessorType;

+import javax.xml.bind.annotation.XmlRootElement;

+

+import com.google.common.base.MoreObjects;

+

+/**

+ * 

+ * @author Johannes Stamm, Peak Solution GmbH

+ *

+ */

+@XmlRootElement

+@XmlAccessorType(XmlAccessType.FIELD)

+public class PreferenceList {

+

+	private List<PreferenceMessage> preferences;

+	

+	public PreferenceList(List<PreferenceMessage> preferenceList) {

+		preferences = preferenceList;

+	}

+

+	public List<PreferenceMessage> getPreferences() {

+		return preferences;

+	}

+	

+	@Override

+	public boolean equals(Object obj) {

+

+		if (obj == null) {

+			return false;

+		}

+		if (getClass() != obj.getClass()) {

+			return false;

+		}

+

+		final PreferenceList other = (PreferenceList) obj;

+

+		return Objects.equals(this.preferences, other.preferences);

+	}

+	

+	@Override

+	public int hashCode() {

+		return Objects.hash(preferences);

+	}

+	

+	@Override

+	public String toString() {

+		return MoreObjects.toStringHelper(PreferenceList.class)

+				.add("preferences", preferences)

+				.toString();

+	}

+}

diff --git a/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/PreferenceMessage.java b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/PreferenceMessage.java
new file mode 100644
index 0000000..099a056
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/PreferenceMessage.java
@@ -0,0 +1,117 @@
+/*******************************************************************************

+  * Copyright (c) 2017 Peak Solution GmbH

+  * All rights reserved. This program and the accompanying materials

+  * are made available under the terms of the Eclipse Public License v1.0

+  * which accompanies this distribution, and is available at

+  * http://www.eclipse.org/legal/epl-v10.html

+  *

+  * Contributors:

+  * Johannes Stamm - initial implementation

+  *******************************************************************************/

+package org.eclipse.mdm.preferences.entity;

+

+import java.util.Objects;

+

+import javax.xml.bind.annotation.XmlRootElement;

+

+import org.apache.commons.lang3.builder.EqualsBuilder;

+

+import com.google.common.base.MoreObjects;

+

+/**

+ * 

+ * @author Johannes Stamm, Peak Solution GmbH

+ *

+ */

+@XmlRootElement(name="preference")

+public class PreferenceMessage {

+

+	public enum Scope {

+		SYSTEM,

+		SOURCE,

+		USER;

+	}

+	

+	private Scope scope;

+	private String source;

+	private String user;

+	private String key;

+	private String value;

+	private Long id;

+	

+	public Scope getScope() {

+		return scope;

+	}

+	public void setScope(Scope scope) {

+		this.scope = scope;

+	}

+	public String getSource() {

+		return source;

+	}

+	public void setSource(String source) {

+		this.source = source;

+	}

+	public String getUser() {

+		return user;

+	}

+	public void setUser(String user) {

+		this.user = user;

+	}

+	public String getKey() {

+		return key;

+	}

+	public void setKey(String key) {

+		this.key = key;

+	}

+	public String getValue() {

+		return value;

+	}

+	public void setValue(String value) {

+		this.value = value;

+	}

+	public Long getId() {

+		return id;

+	}

+	public void setId(Long id) {

+		this.id = id;

+	}

+	

+	@Override

+	public boolean equals(Object obj) {

+

+		if (obj == null) {

+			return false;

+		}

+		if (getClass() != obj.getClass()) {

+			return false;

+		}

+

+		final PreferenceMessage other = (PreferenceMessage) obj;

+

+		return new EqualsBuilder()

+				.append(this.id, other.id)

+				.append(this.scope, other.scope)

+				.append(this.source, other.source)

+				.append(this.user, other.user)

+				.append(this.key, other.key)

+				.append(this.value, other.value)

+				.isEquals();

+	}

+	

+	@Override

+	public int hashCode() {

+		return Objects.hash(id, scope, source, user, key, value);

+	}

+	

+	@Override

+	public String toString() {

+		return MoreObjects.toStringHelper(PreferenceMessage.class)

+				.add("id", id)

+				.add("scope", scope)

+				.add("source", source)

+				.add("user", user)

+				.add("key", key)

+				.add("value", value)

+				.toString();

+	}

+}

diff --git a/org.eclipse.mdm.preferences/src/main/resources/META-INF/persistence.xml b/org.eclipse.mdm.preferences/src/main/resources/META-INF/persistence.xml
new file mode 100644
index 0000000..f1fa4ff
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- 
+/*******************************************************************************
+ * Copyright (c) 2017 Peak Solution GmbH
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Koller - initial implementation
+ *******************************************************************************/
+ -->
+<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
+	version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
+	<persistence-unit name="openMDM" transaction-type="JTA">
+		<jta-data-source>jdbc/openMDM</jta-data-source>
+		<class>org.eclipse.mdm.preferences.entity.Preference</class>
+		<properties>
+			<property name="eclipselink.logging.logger" value="ServerLogger" />
+			<property name="eclipselink.logging.level" value="INFO" />
+			<property name="eclipselink.ddl-generation" value="none" />
+		</properties>
+	</persistence-unit>
+</persistence>	
\ No newline at end of file
diff --git a/org.eclipse.mdm.preferences/src/test/java/org/eclipse/mdm/preferences/controller/PreferenceServiceTest.java b/org.eclipse.mdm.preferences/src/test/java/org/eclipse/mdm/preferences/controller/PreferenceServiceTest.java
new file mode 100644
index 0000000..df6da88
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/test/java/org/eclipse/mdm/preferences/controller/PreferenceServiceTest.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+  * Copyright (c) 2017 Peak Solution GmbH
+  * All rights reserved. This program and the accompanying materials
+  * are made available under the terms of the Eclipse Public License v1.0
+  * which accompanies this distribution, and is available at
+  * http://www.eclipse.org/legal/epl-v10.html
+  *
+  * Contributors:
+  * Johannes Stamm - initial implementation
+  *******************************************************************************/
+package org.eclipse.mdm.preferences.controller;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.security.Principal;
+import java.util.List;
+
+import javax.ejb.SessionContext;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
+import org.assertj.core.groups.Tuple;
+import org.eclipse.mdm.preferences.entity.Preference;
+import org.eclipse.mdm.preferences.entity.PreferenceMessage;
+import org.eclipse.mdm.preferences.entity.PreferenceMessage.Scope;
+import org.eclipse.persistence.config.PersistenceUnitProperties;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class PreferenceServiceTest
+{
+	private EntityManagerFactory factory;
+	private EntityManager em;
+	private SessionContext sessionContext = mock(SessionContext.class);
+	private PreferenceService service;
+	
+	@Before
+    public void init() {
+		factory = Persistence.createEntityManagerFactory("preferenceTest", 
+				ImmutableMap.of(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML, "META-INF/persistence-test.xml"));
+		
+		em = factory.createEntityManager();
+		
+		Principal principal = mock(Principal.class);
+		when(principal.getName()).thenReturn("testUser");
+		when(sessionContext.getCallerPrincipal()).thenReturn(principal);
+		
+		service  = new PreferenceService(em, sessionContext);
+    }
+
+    @After
+    public void destroy() {
+    	factory.close();
+    }
+    
+	private void initData(Preference... preferences) {
+		EntityManager em = factory.createEntityManager();
+		em.getTransaction().begin();
+		em.createQuery("delete from Preference").executeUpdate();
+		for (Preference p : preferences) {
+			em.persist(p);
+		}
+	
+		em.getTransaction().commit();
+		em.close();
+	}
+
+	@Test
+	public void testGetPreferences() {
+		initData(new Preference(null, null, "testGetPreferences", "myValue1"),
+				new Preference("MDMTEST", null, "testGetPreferences", "myValue2"),
+				new Preference(null, "testUser", "testGetPreferences", "myValue3"),
+				new Preference(null, "otherUser", "testGetPreferences", "myValue4"),
+				new Preference(null, null, "otherKey", "myValue5"));
+		
+		
+		assertThat(service.getPreferences(null, "testGetPreferences"))
+			.extracting("scope", "source", "user", "key", "value")
+			.containsExactlyInAnyOrder(
+					new Tuple(Scope.SYSTEM, null, null, "testGetPreferences", "myValue1"),
+					new Tuple(Scope.SOURCE, "MDMTEST", null, "testGetPreferences", "myValue2"),
+					new Tuple(Scope.USER, null, "testUser", "testGetPreferences", "myValue3"));
+	}
+	
+	
+	@Test
+	public void testGetPreferencesForSystem() {
+		initData(new Preference(null, null, "testGetPreferencesSystem", "myValue"),
+				new Preference("MDMTEST", null, "testGetPreferencesSystem", "myValue"));
+		
+		
+		assertThat(service.getPreferences("system", "testGetPreferencesSystem"))
+			.extracting("scope", "source", "user", "key", "value")
+			.containsExactly(new Tuple(Scope.SYSTEM, null, null, "testGetPreferencesSystem", "myValue"));
+	}
+	
+	@Test
+	public void testGetPreferencesForSource() {
+		initData(new Preference(null, "testUser", "testGetPreferencesForSource", "myValue"),
+				new Preference("MDM_OTHER", null, "testGetPreferencesForSource", "myValue"),
+				new Preference("MDMTEST", null, "testGetPreferencesForSource", "myValue"));
+		
+		
+		assertThat(service.getPreferences("source", "testGetPreferencesForSource"))
+			.extracting("scope", "source", "user", "key", "value")
+			.containsExactlyInAnyOrder(
+					new Tuple(Scope.SOURCE, "MDM_OTHER", null, "testGetPreferencesForSource", "myValue"),
+					new Tuple(Scope.SOURCE, "MDMTEST", null, "testGetPreferencesForSource", "myValue"));
+	}
+	
+	@Test
+	public void testGetPreferencesForUser() {
+		initData(new Preference(null, "other", "testGetPreferencesForUser", "myValue"),
+				new Preference(null, "testUser", "testGetPreferencesForUser", "myValue"),
+				new Preference("MDMTEST", null, "testGetPreferencesForUser", "myValue"));
+		
+		
+		assertThat(service.getPreferences("user", "testGetPreferencesForUser"))
+			.extracting("scope", "source", "user", "key", "value")
+			.containsExactly(
+					new Tuple(Scope.USER, null, "other", "testGetPreferencesForUser", "myValue"),
+					new Tuple(Scope.USER, null, "testUser", "testGetPreferencesForUser", "myValue"));
+	}
+	
+	@Test
+	public void testGetPreferencesBySource() {
+		initData(new Preference("MDMTEST", null, "testGetPreferencesSource", "myValue"),
+				new Preference("MDM_OTHER", null, "testGetPreferencesSource", "myValue"));
+		
+		assertThat(service.getPreferencesBySource("MDMTEST", "testGetPreferencesSource"))
+			.hasSize(1)
+			.extracting("scope", "source", "user", "key", "value")
+			.containsExactly(new Tuple(Scope.SOURCE, "MDMTEST", null, "testGetPreferencesSource", "myValue"));
+	}
+	
+	@Test
+	public void testGetPreferencesBySourceKeyEmpty() {
+		initData(new Preference("MDMTEST", null, "testGetPreferencesSourceKeyEmpty", "myValue"),
+				new Preference("MDM_OTHER", null, "testGetPreferencesSourceKeyEmpty", "myValue"));
+		
+		assertThat(service.getPreferencesBySource("MDMTEST", ""))
+			.hasSize(1)
+			.extracting("scope", "source", "user", "key", "value")
+			.containsExactly(new Tuple(Scope.SOURCE, "MDMTEST", null, "testGetPreferencesSourceKeyEmpty", "myValue"));
+	}
+	
+	@Test
+	public void testDeletePreference() {
+		initData(new Preference(null, null, "testDeletePreference", "myValue"));
+		
+		List<PreferenceMessage> listBeforeDelete = service.getPreferences("system", "testDeletePreference");
+		
+		assertThat(listBeforeDelete).hasSize(1);
+		
+		em.getTransaction().begin();
+		service.deletePreference(listBeforeDelete.get(0).getId());
+		em.getTransaction().commit();
+		
+		
+		List<PreferenceMessage> listAfterDelete = service.getPreferences("system", "testDeletePreference");
+		assertThat(listAfterDelete).hasSize(0);
+	}
+	
+	@Test
+	public void testSaveSystemScope() {
+		PreferenceMessage pref = new PreferenceMessage();
+		pref.setScope(Scope.SYSTEM);
+		pref.setKey("testSaveSystemScope");
+		pref.setValue("myValue");
+		
+		em.getTransaction().begin();
+		PreferenceMessage saved = service.save(pref);
+		em.getTransaction().commit();
+		
+		assertThat(saved)
+			.hasNoNullFieldsOrPropertiesExcept("source", "user")
+			.hasFieldOrPropertyWithValue("scope", Scope.SYSTEM)
+			.hasFieldOrPropertyWithValue("key", "testSaveSystemScope")
+			.hasFieldOrPropertyWithValue("value", "myValue");
+	}
+	
+	@Test
+	public void testSaveSourceScope() {
+		PreferenceMessage pref = new PreferenceMessage();
+		pref.setScope(Scope.SOURCE);
+		pref.setSource("MDMTEST");
+		pref.setKey("testSaveSourceScope");
+		pref.setValue("myValue");
+		
+		em.getTransaction().begin();
+		PreferenceMessage saved = service.save(pref);
+		em.getTransaction().commit();
+		
+		assertThat(saved)
+			.hasNoNullFieldsOrPropertiesExcept("user")
+			.hasFieldOrPropertyWithValue("scope", Scope.SOURCE)
+			.hasFieldOrPropertyWithValue("source", "MDMTEST")
+			.hasFieldOrPropertyWithValue("key", "testSaveSourceScope")
+			.hasFieldOrPropertyWithValue("value", "myValue");
+	}
+
+	@Test
+	public void testSaveUserScope() {
+		PreferenceMessage pref = new PreferenceMessage();
+		pref.setScope(Scope.USER);
+		pref.setKey("testSaveUserScope");
+		pref.setValue("myValue");
+		
+		em.getTransaction().begin();
+		PreferenceMessage saved = service.save(pref);
+		em.getTransaction().commit();
+		
+		assertThat(saved)
+			.hasNoNullFieldsOrPropertiesExcept("source")
+			.hasFieldOrPropertyWithValue("scope", Scope.USER)
+			.hasFieldOrPropertyWithValue("user", "testUser")
+			.hasFieldOrPropertyWithValue("key", "testSaveUserScope")
+			.hasFieldOrPropertyWithValue("value", "myValue");
+	}
+	
+	@Test
+	public void testSaveOverrideExisting() {
+		initData(new Preference(null, null, "testSaveOverrideExisting", "myValue"));
+		
+		PreferenceMessage pref = new PreferenceMessage();
+		pref.setScope(Scope.SYSTEM);
+		pref.setKey("testSaveOverrideExisting");
+		pref.setValue("myValue");
+		
+		em.getTransaction().begin();
+		PreferenceMessage saved = service.save(pref);
+		em.getTransaction().commit();
+		
+		assertThat(saved)
+			.hasNoNullFieldsOrPropertiesExcept("source", "user")
+			.hasFieldOrPropertyWithValue("scope", Scope.SYSTEM)
+			.hasFieldOrPropertyWithValue("key", "testSaveOverrideExisting")
+			.hasFieldOrPropertyWithValue("value", "myValue");
+		
+		assertThat(service.getPreferences("System", "testSaveOverrideExisting")).hasSize(1);
+	}
+}
diff --git a/org.eclipse.mdm.preferences/src/test/java/org/eclipse/mdm/preferences/entity/PreferenceTest.java b/org.eclipse.mdm.preferences/src/test/java/org/eclipse/mdm/preferences/entity/PreferenceTest.java
new file mode 100644
index 0000000..a36dbf4
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/test/java/org/eclipse/mdm/preferences/entity/PreferenceTest.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+  * Copyright (c) 2017 Peak Solution GmbH
+  * All rights reserved. This program and the accompanying materials
+  * are made available under the terms of the Eclipse Public License v1.0
+  * which accompanies this distribution, and is available at
+  * http://www.eclipse.org/legal/epl-v10.html
+  *
+  * Contributors:
+  * Johannes Stamm - initial implementation
+  *******************************************************************************/
+package org.eclipse.mdm.preferences.entity;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import javax.persistence.TypedQuery;
+
+import org.eclipse.persistence.config.PersistenceUnitProperties;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableMap;
+
+public class PreferenceTest
+{
+	private static final long CLOB_SIZE = 1_000_000;
+
+	private EntityManagerFactory factory ;
+
+	@Before
+    public void init() {
+		factory = Persistence.createEntityManagerFactory("preferenceTest", 
+				ImmutableMap.of(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML, "META-INF/persistence-test.xml"));
+		
+		EntityManager em = factory.createEntityManager();
+		em.getTransaction().begin();
+		em.createQuery("delete from Preference").executeUpdate();
+		em.getTransaction().commit();
+		em.close();
+    }
+
+    @After
+    public void destroy() {
+    	factory.close();
+    }
+    
+	@Test
+	public void testPersist() {
+		
+		EntityManager em = factory.createEntityManager();
+
+		em.getTransaction().begin();
+		Preference p = new Preference("MDMXYZ", "*", "key1", "value1");
+		em.persist(p);
+		em.getTransaction().commit();
+	}
+
+	@Test
+	public void testLoad() {
+		EntityManager em = factory.createEntityManager();
+
+		em.getTransaction().begin();
+		Preference p = new Preference("MDMXYZ", "*", "key2", "value1");
+		em.persist(p);
+		em.getTransaction().commit();
+
+		assertThat(em.find(Preference.class, p.getId()))
+		.isEqualToIgnoringGivenFields(new Preference("MDMXYZ", "*", "key2", "value1"), "id");
+
+		em.close();
+	}
+
+	@Test
+	public void testQuery() {
+		EntityManager em = factory.createEntityManager();
+
+		em.getTransaction().begin();
+		Preference p = new Preference("MDMXYZ", "*", "key3", "value1");
+		em.persist(p);
+		em.getTransaction().commit();
+
+		TypedQuery<Preference> q = em.createQuery("select p from Preference p", Preference.class);
+		assertThat(q.getResultList())
+		.hasSize(1)
+		.usingElementComparatorIgnoringFields("id")
+		.contains(new Preference("MDMXYZ", "*", "key3", "value1"));
+
+		em.close();
+	}
+
+	@Test
+	public void testPersistLob() {
+		String longString = generateString(CLOB_SIZE);
+
+		EntityManager em = factory.createEntityManager();
+
+		em.getTransaction().begin();
+		Preference p = new Preference("MDMXYZ", "*", "clob", longString);
+		em.persist(p);
+		em.getTransaction().commit();
+		em.close();
+
+		em = factory.createEntityManager();
+		Preference loaded = em.find(Preference.class, p.getId());
+
+		assertThat(loaded).isEqualToIgnoringGivenFields(new Preference("MDMXYZ", "*", "clob", longString), "id");
+		em.close();
+	}
+
+	private String generateString(long length) {
+		StringBuilder builder = new StringBuilder();
+		for (int i = 0; i < length; i++)
+		{
+			builder.append("a");
+		}
+		return builder.toString();
+	}
+}
diff --git a/org.eclipse.mdm.preferences/src/test/resources/META-INF/persistence-test.xml b/org.eclipse.mdm.preferences/src/test/resources/META-INF/persistence-test.xml
new file mode 100644
index 0000000..0fc6a55
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/test/resources/META-INF/persistence-test.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- 
+/*******************************************************************************
+ * Copyright (c) 2017 Peak Solution GmbH
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Matthias Koller - initial implementation
+ *******************************************************************************/
+ -->
+<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
+	version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
+	<persistence-unit name="preferenceTest" transaction-type="RESOURCE_LOCAL">
+
+		<class>org.eclipse.mdm.preferences.entity.Preference</class>
+
+		<properties>
+			<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
+			<property name="javax.persistence.jdbc.url" value="jdbc:derby:memory:myDB;create=true" />
+			<property name="javax.persistence.jdbc.user" value="EclipseJPAExample" />
+			<property name="javax.persistence.jdbc.password" value="EclipseJPAExample" />
+
+			<!-- EclipseLink should create the database schema automatically -->
+			<property name="eclipselink.ddl-generation" value="create-tables" />
+			<property name="eclipselink.ddl-generation.output-mode" value="database" />
+
+			<property name="eclipselink.logging.level.sql" value="FINER" />
+			<property name="eclipselink.logging.parameters" value="true" />
+		</properties>
+
+	</persistence-unit>
+</persistence>	
\ No newline at end of file
diff --git a/org.eclipse.mdm.property/src/main/configuration/global.properties b/org.eclipse.mdm.property/src/main/configuration/global.properties
index 4f2b877..bde6281 100644
--- a/org.eclipse.mdm.property/src/main/configuration/global.properties
+++ b/org.eclipse.mdm.property/src/main/configuration/global.properties
@@ -35,6 +35,8 @@
 #after reregistration 

 freetext.notificationName=someUniqueName

 

+# Specifies the maximum number of results per source returned by the query endpoint.

+businessobjects.query.maxresultspersource=1001

 

 # ------------------------------

 # configuration for file release process (PAK2RAW and PAK2ATFX)

diff --git a/settings.gradle b/settings.gradle
index de0ce9e..3c78c08 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -17,4 +17,5 @@
 include 'org.eclipse.mdm.filerelease'
 include 'org.eclipse.mdm.property'
 include 'org.eclipse.mdm.freetextindexer'
+include 'org.eclipse.mdm.preferences'