Get 5.0.0 for org.eclipse.mdm.nucleus
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ffd6c50
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+# eclipse
+.classpath
+.project
+.settings/
+bin/
+
+# gradle
+.gradle
+build/
+gradle.properties
+
+# intellij
+.idea/
+out/
+*.ipr
+*.iml
+*.iws
+/bin/
+
+# node
+**/node_modules
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..e48e096
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,277 @@
+Eclipse Public License - v 2.0
+
+    THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+    PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+    OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+  a) in the case of the initial Contributor, the initial content
+     Distributed under this Agreement, and
+
+  b) in the case of each subsequent Contributor:
+     i) changes to the Program, and
+     ii) additions to the Program;
+  where such changes and/or additions to the Program originate from
+  and are Distributed by that particular Contributor. A Contribution
+  "originates" from a Contributor if it was added to the Program by
+  such Contributor itself or anyone acting on such Contributor's behalf.
+  Contributions do not include changes or additions to the Program that
+  are not Modified Works.
+
+"Contributor" means any person or entity that Distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which
+are necessarily infringed by the use or sale of its Contribution alone
+or when combined with the Program.
+
+"Program" means the Contributions Distributed in accordance with this
+Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement
+or any Secondary License (as applicable), including Contributors.
+
+"Derivative Works" shall mean any work, whether in Source Code or other
+form, that is based on (or derived from) the Program and for which the
+editorial revisions, annotations, elaborations, or other modifications
+represent, as a whole, an original work of authorship.
+
+"Modified Works" shall mean any work in Source Code or other form that
+results from an addition to, deletion from, or modification of the
+contents of the Program, including, for purposes of clarity any new file
+in Source Code form that contains any contents of the Program. Modified
+Works shall not include works that contain only declarations,
+interfaces, types, classes, structures, or files of the Program solely
+in each case in order to link to, bind by name, or subclass the Program
+or Modified Works thereof.
+
+"Distribute" means the acts of a) distributing or b) making available
+in any manner that enables the transfer of a copy.
+
+"Source Code" means the form of a Program preferred for making
+modifications, including but not limited to software source code,
+documentation source, and configuration files.
+
+"Secondary License" means either the GNU General Public License,
+Version 2.0, or any later versions of that license, including any
+exceptions or additional permissions as identified by the initial
+Contributor.
+
+2. GRANT OF RIGHTS
+
+  a) Subject to the terms of this Agreement, each Contributor hereby
+  grants Recipient a non-exclusive, worldwide, royalty-free copyright
+  license to reproduce, prepare Derivative Works of, publicly display,
+  publicly perform, Distribute and sublicense the Contribution of such
+  Contributor, if any, and such Derivative Works.
+
+  b) Subject to the terms of this Agreement, each Contributor hereby
+  grants Recipient a non-exclusive, worldwide, royalty-free patent
+  license under Licensed Patents to make, use, sell, offer to sell,
+  import and otherwise transfer the Contribution of such Contributor,
+  if any, in Source Code or other form. This patent license shall
+  apply to the combination of the Contribution and the Program if, at
+  the time the Contribution is added by the Contributor, such addition
+  of the Contribution causes such combination to be covered by the
+  Licensed Patents. The patent license shall not apply to any other
+  combinations which include the Contribution. No hardware per se is
+  licensed hereunder.
+
+  c) Recipient understands that although each Contributor grants the
+  licenses to its Contributions set forth herein, no assurances are
+  provided by any Contributor that the Program does not infringe the
+  patent or other intellectual property rights of any other entity.
+  Each Contributor disclaims any liability to Recipient for claims
+  brought by any other entity based on infringement of intellectual
+  property rights or otherwise. As a condition to exercising the
+  rights and licenses granted hereunder, each Recipient hereby
+  assumes sole responsibility to secure any other intellectual
+  property rights needed, if any. For example, if a third party
+  patent license is required to allow Recipient to Distribute the
+  Program, it is Recipient's responsibility to acquire that license
+  before distributing the Program.
+
+  d) Each Contributor represents that to its knowledge it has
+  sufficient copyright rights in its Contribution, if any, to grant
+  the copyright license set forth in this Agreement.
+
+  e) Notwithstanding the terms of any Secondary License, no
+  Contributor makes additional grants to any Recipient (other than
+  those set forth in this Agreement) as a result of such Recipient's
+  receipt of the Program under the terms of a Secondary License
+  (if permitted under the terms of Section 3).
+
+3. REQUIREMENTS
+
+3.1 If a Contributor Distributes the Program in any form, then:
+
+  a) the Program must also be made available as Source Code, in
+  accordance with section 3.2, and the Contributor must accompany
+  the Program with a statement that the Source Code for the Program
+  is available under this Agreement, and informs Recipients how to
+  obtain it in a reasonable manner on or through a medium customarily
+  used for software exchange; and
+
+  b) the Contributor may Distribute the Program under a license
+  different than this Agreement, provided that such license:
+     i) effectively disclaims on behalf of all other Contributors all
+     warranties and conditions, express and implied, including
+     warranties or conditions of title and non-infringement, and
+     implied warranties or conditions of merchantability and fitness
+     for a particular purpose;
+
+     ii) effectively excludes on behalf of all other Contributors all
+     liability for damages, including direct, indirect, special,
+     incidental and consequential damages, such as lost profits;
+
+     iii) does not attempt to limit or alter the recipients' rights
+     in the Source Code under section 3.2; and
+
+     iv) requires any subsequent distribution of the Program by any
+     party to be under a license that satisfies the requirements
+     of this section 3.
+
+3.2 When the Program is Distributed as Source Code:
+
+  a) it must be made available under this Agreement, or if the
+  Program (i) is combined with other material in a separate file or
+  files made available under a Secondary License, and (ii) the initial
+  Contributor attached to the Source Code the notice described in
+  Exhibit A of this Agreement, then the Program may be made available
+  under the terms of such Secondary Licenses, and
+
+  b) a copy of this Agreement must be included with each copy of
+  the Program.
+
+3.3 Contributors may not remove or alter any copyright, patent,
+trademark, attribution notices, disclaimers of warranty, or limitations
+of liability ("notices") contained within the Program from any copy of
+the Program which they Distribute, provided that Contributors may add
+their own appropriate notices.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities
+with respect to end users, business partners and the like. While this
+license is intended to facilitate the commercial use of the Program,
+the Contributor who includes the Program in a commercial product
+offering should do so in a manner which does not create potential
+liability for other Contributors. Therefore, if a Contributor includes
+the Program in a commercial product offering, such Contributor
+("Commercial Contributor") hereby agrees to defend and indemnify every
+other Contributor ("Indemnified Contributor") against any losses,
+damages and costs (collectively "Losses") arising from claims, lawsuits
+and other legal actions brought by a third party against the Indemnified
+Contributor to the extent caused by the acts or omissions of such
+Commercial Contributor in connection with its distribution of the Program
+in a commercial product offering. The obligations in this section do not
+apply to any claims or Losses relating to any actual or alleged
+intellectual property infringement. In order to qualify, an Indemnified
+Contributor must: a) promptly notify the Commercial Contributor in
+writing of such claim, and b) allow the Commercial Contributor to control,
+and cooperate with the Commercial Contributor in, the defense and any
+related settlement negotiations. The Indemnified Contributor may
+participate in any such claim at its own expense.
+
+For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those performance
+claims and warranties, and if a court requires any other Contributor to
+pay any damages as a result, the Commercial Contributor must pay
+those damages.
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
+BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
+TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+PURPOSE. Each Recipient is solely responsible for determining the
+appropriateness of using and distributing the Program and assumes all
+risks associated with its exercise of rights under this Agreement,
+including but not limited to the risks and costs of program errors,
+compliance with applicable laws, damage to or loss of data, programs
+or equipment, and unavailability or interruption of operations.
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further
+action by the parties hereto, such provision shall be reformed to the
+minimum extent necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other software
+or hardware) infringes such Recipient's patent(s), then such Recipient's
+rights granted under Section 2(b) shall terminate as of the date such
+litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of
+time after becoming aware of such noncompliance. If all Recipient's
+rights under this Agreement terminate, Recipient agrees to cease use
+and distribution of the Program as soon as reasonably practicable.
+However, Recipient's obligations under this Agreement and any licenses
+granted by Recipient relating to the Program shall continue and survive.
+
+Everyone is permitted to copy and distribute copies of this Agreement,
+but in order to avoid inconsistency the Agreement is copyrighted and
+may only be modified in the following manner. The Agreement Steward
+reserves the right to publish new versions (including revisions) of
+this Agreement from time to time. No one other than the Agreement
+Steward has the right to modify this Agreement. The Eclipse Foundation
+is the initial Agreement Steward. The Eclipse Foundation may assign the
+responsibility to serve as the Agreement Steward to a suitable separate
+entity. Each new version of the Agreement will be given a distinguishing
+version number. The Program (including Contributions) may always be
+Distributed subject to the version of the Agreement under which it was
+received. In addition, after a new version of the Agreement is published,
+Contributor may elect to Distribute the Program (including its
+Contributions) under the new version.
+
+Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
+receives no rights or licenses to the intellectual property of any
+Contributor under this Agreement, whether expressly, by implication,
+estoppel or otherwise. All rights in the Program not expressly granted
+under this Agreement are reserved. Nothing in this Agreement is intended
+to be enforceable by any entity that is not a Contributor or Recipient.
+No third-party beneficiary rights are created under this Agreement.
+
+Exhibit A - Form of Secondary Licenses Notice
+
+"This Source Code may also be made available under the following
+Secondary Licenses when the conditions for such availability set forth
+in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
+version(s), and exceptions or additional permissions here}."
+
+  Simply including a copy of this Agreement, including this Exhibit A
+  is not sufficient to license the Source Code under Secondary Licenses.
+
+  If it is not possible or desirable to put the notice in a particular
+  file, then You may include the notice in a location (such as a LICENSE
+  file in a relevant directory) where a recipient would be likely to
+  look for such a notice.
+
+  You may add additional accurate notices of copyright ownership.
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 0000000..8bbd2e1
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,341 @@
+# Notices for Eclipse MDM|BL
+
+This content is produced and maintained by the Eclipse MDM|BL project,
+it is a project of the openMDM(R) Eclipse Working Group.
+
+* Project home: https://projects.eclipse.org/projects/technology.mdmbl
+
+## Trademarks
+
+Eclipse MDM|BL, MDM|BL and Eclipse openMDM(R) logo are registered trademarks
+of the Eclipse Foundation.
+
+## Copyright
+
+All content is the property of the following respective authors or their employers.
+For more information regarding authorship of content, please consult the listed
+source code repository logs.
+
+Copyright (c) 2016-2018 Gigatronik Ingolstadt GmbH
+Copyright (c) 2016-2018 Peak Solution GmbH
+Copyright (c) 2017-2018 science + computing AG Tuebingen (ATOS SE)
+Copyright (c) 2017-2018 Canoo Engineering AG
+Copyright (c) 2017 Florian Schmitt
+Copyright (c) 2017-2018 Angelika Wittek
+Copyright (c) 2018 Elektronische Fahrwerksysteme GMBH
+Copyright (c) 2018 Karakun AG
+
+## Declared Project Licenses
+
+This program and the accompanying materials are made available under the
+terms of the Eclipse Public License v. 2.0 which is available at
+http://www.eclipse.org/legal/epl-2.0.
+
+SPDX-License-Identifier: EPL-2.0
+
+## Source Code
+
+The project maintains the following source code repositories:
+
+org.eclipse.mdm.api.base.git	      - The openMDM(R) API.
+org.eclipse.mdm.api.default.git	    - Extension of the openMDM(R) API containing default elements.
+org.eclipse.mdm.api.odsadapter.git	- ODS implementation of persistence adapter.
+org.eclipse.mdm.nucleus.git	        - Core building blocks for the openMDM Business Logic and Web Frontend.
+
+## Third-party Content
+
+The Content includes items that have been sourced from third parties as set out below.
+If you did not receive this Content directly from the Eclipse Foundation, the following
+is provided for informational purposes only, and you should look to
+the Redistributor's license for terms and conditions of use.
+
+antlr4-4.5.3.jar(2.5.3)
+    * License: New BSD license
+
+aopalliance-repackaged-2.5.0-b05.jar (2.5.0-b05)
+    * License: CDDL
+
+commons-codec-1.2.jar (1.2)
+    * License: Apache License, 2.0
+
+commons-httpclient-3.1.jar  (3.1)
+    * License: Apache License, 2.0
+
+commons-lang3-3.4.jar (3.4)
+    * License: Apache License, 2.0
+
+gson-2.7.jar (2.7)
+    * License: Apache License, 2.0
+
+Google Guava Version: 25.0-jre  (25.0)
+    * License: Apache License, 2.0
+    
+Gradle Wrapper (4.10.2)
+    * License: Apache License, 2.0    
+
+hk2-api-2.5.0-b05.jar (2.5.0-b05)
+    * License: CDDL-1.1
+
+hk2-locator-2.5.0-b05.jar(2.5.0-b05)
+    * License: CDDL
+
+hk2-utils-2.5.0-b05.jar  (2.5.0-b05)
+    * License: CDDL
+
+jackson-annotations-2.9.0.jar  (2.9.0)
+    * License: Apache License, 2.0
+
+jackson-core-2.9.2.jar(2.9.2)
+    * License: Apache License, 2.0
+
+jackson-databind-2.9.2.jar  (2.9.2)
+    * License: Apache License, 2.0
+
+jackson-jaxrs-base-2.9.2.jar(2.9.2)
+    * License: Apache License, 2.0
+
+jackson-jaxrs-json-provider-2.9.2.jar(2.9.2)
+    * License: Apache License, 2.0
+
+jackson-module-jaxb-annotations-2.9.2.jar  (2.9.2)
+    * License: Apache License, 2.0
+
+javassist-3.20.0-GA.jar  (2.20.0-GA)
+    * License: Apache 2.0
+
+jcl-over-slf4j-1.7.25.jar(1.7.25)
+    * License: MIT License
+    * Licence Path: https://www.slf4j.org/license.html
+    * Project URL:  https://www.slf4j.org
+    * Source URL:   https://github.com/qos-ch/slf4j
+
+jersey-client-2.23.2.jar (2.23.2)
+     * License: CDDL
+
+jersey-common-2.23.2.jar (2.23.2)
+    * License: CDDL
+
+jersey-container-servlet-2.23.2.jar  (2.23.2)
+    * License: CDDL
+
+jersey-container-servlet-core-2.23.3.jar(2.23.2)
+    * License: CDDL
+
+jersey-guava-2.23.2.jar  (2.23.2)
+    * License: Apache License, 2.0
+
+jersey-media-jaxb-2.23.2.jar(2.23.2)
+    * License: CDDL
+
+jersey-media-sse-2.23.2.jar (2.23.2)
+    * License: CDDL
+
+jersey-server-2.23.2.jar (2.23.2)
+    * License: Apache-2.0
+
+logback-classic-1.2.3.jar(1.2.3)
+    * License: Eclipse Public License 1.0
+
+logback-core-1.2.3.jar(1.2.3)
+    * License: Eclipse Public License 1.0
+
+osgi-resource-locator-1.0.1.jar(1.0.1)
+    * License: CDDL
+
+protobuf-java-3.2.0.jar (3.2.0)
+    * License: New BSD license
+
+protobuf-java-util-3.2.0.jar  (3.2.0)
+    * License: New BSD license
+
+slf4j-api-1.7.25.jar  (1.7.25)
+    * License: MIT license
+    * Licence Path: https://www.slf4j.org/license.html
+    * Project URL: https://github.com/qos-ch/slf4j
+    * Source URL:  https://github.com/qos-ch/slf4j/releases/tag/v_1.7.25
+
+validation-api-1.1.0.Final.jar (1.1.0.Final)
+* License: Apache License, 2.0
+
+vavr-0.9.1-sources.jar (0.9.1)
+* License: Apache License, 2.0
+
+@angular/common@2.4.8
+    * License: MIT
+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE
+    * Project: https://github.com/angular/angular
+    * Source:  https://github.com/angular/angular
+
+@angular/compiler@2.4.8
+    * License: MIT
+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE
+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE
+    * Project: https://github.com/angular/angular
+    * Source:  https://github.com/angular/angular
+
+@angular/core@2.4.8
+    * License: MIT
+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE
+    * Project: https://github.com/angular/angular
+    * Source:  https://github.com/angular/angular
+
+@angular/forms@2.4.8
+    * License: MIT
+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE
+    * Project: https://github.com/angular/angular
+    * Source:  https://github.com/angular/angular
+
+@angular/http@2.4.8
+    * License: MIT
+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE
+    * Project: https://github.com/angular/angular
+    * Source:  https://github.com/angular/angular
+
+@angular/platform-browser-dynamic@2.4.8
+    * License: MIT
+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE
+    * Project: https://github.com/angular/angular
+    * Source:  https://github.com/angular/angular
+
+@angular/platform-browser@2.4.8
+    * License: MIT
+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE
+    * Project: https://github.com/angular/angular
+    * Source:  https://github.com/angular/angular
+
+@angular/router@3.4.8
+    * License: MIT
+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE
+    * Project: https://github.com/angular/angular
+    * Source:  https://github.com/angular/angular
+
+@types/file-saver@0.0.0
+    * License: MIT
+    * Licence Path: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/LICENSE
+    * Project: https://github.com/DefinitelyTyped/DefinitelyTyped
+    * Source:  https://github.com/DefinitelyTyped/DefinitelyTyped
+
+bootstrap@3.3.7
+    * License: MIT
+    * Licence Path: https://github.com/twbs/bootstrap/raw/master/LICENSE
+    * Project: https://github.com/twbs/bootstrap
+    * Source:  https://github.com/twbs/bootstrap
+
+class-transformer@0.1.6
+    * License: MIT
+    * Licence Path: https://github.com/typestack/class-transformer/blob/master/LICENSE
+    * Project: https://github.com/pleerock/class-transformer
+    * Source:  https://github.com/pleerock/class-transformer
+
+core-js@2.4.1
+    * License: MIT
+    * Licence Path: https://github.com/zloirock/core-js/raw/master/LICENSE
+    * Project: https://github.com/zloirock/core-js
+    * Source:  https://github.com/zloirock/core-js
+
+file-saver@1.3.3
+    * License: MIT
+    * Licence Path: https://github.com/eligrey/FileSaver.js/raw/master/LICENSE.md
+    * Project: https://github.com/eligrey/FileSaver.js
+    * Source:  https://github.com/eligrey/FileSaver.js
+
+font-awesome@4.7.0
+    * License: MIT
+    * Licence Path: https://github.com/FortAwesome/Font-Awesome/blob/master/LICENSE.txt
+    * Project: https://fontawesome.com/
+    * Source:  https://github.com/FortAwesome/Font-Awesome
+
+moment@2.17.1
+    * License: MIT
+    * Licence Path: https://github.com/moment/moment/raw/master/LICENSE
+    * Project: https://github.com/moment/moment
+    * Source:  https://github.com/moment/moment
+
+ngx-bootstrap@1.9.1
+    * License: MIT
+    * Licence Path: https://github.com/valor-software/ngx-bootstrap/blob/development/LICENSE
+    * Project: https://valor-software.com/ngx-bootstrap
+    * Source:  https://github.com/valor-software/ngx-bootstrap
+
+ng2-split-pane@1.3.1
+    * License: MIT
+    * Licence Path: https://github.com/wannabegeek/ng2-split-pane/raw/master/LICENSE
+    * Project: https://github.com/wannabegeek/ng2-split-pane
+    * Source:  https://github.com/wannabegeek/ng2-split-pane
+
+primeng@2.0.5
+    * License: MIT
+    * Licence Path: https://github.com/primefaces/primeng/raw/master/LICENSE.md
+    * Project: https://github.com/primefaces/primeng
+    * Source:  https://github.com/primefaces/primeng
+
+reflect-metadata@0.1.3
+    * License: Apache-2.0
+    * Project: https://github.com/rbuckton/ReflectDecorators
+    * Source:  https://github.com/rbuckton/ReflectDecorators
+
+rxjs@5.1.0
+    * License: Apache-2.0
+    * Project: https://github.com/ReactiveX/RxJS
+    * Source:  https://github.com/ReactiveX/RxJS
+
+symbol-observable@1.2.0
+    * License: MIT
+    * Licence Path: https://github.com/blesh/symbol-observable/raw/master/license
+    * Project: https://github.com/blesh/symbol-observable
+    * Source:  https://github.com/blesh/symbol-observable
+
+ts-helpers@1.1.1
+    * License: MIT
+    * Licence Path: https://github.com/ngParty/ts-helpers/raw/master/LICENSE
+    * Project: https://github.com/ngParty/ts-helpers
+    * Source:  https://github.com/ngParty/ts-helpers
+
+zone.js@0.7.6
+    * License: MIT
+    * Licence Path: https://github.com/angular/zone.js/raw/master/LICENSE
+    * Project: https://github.com/angular
+    * Source:  https://github.com/angular/zone.js
+    
+FFAMFAMFAM Silk Icon, Version 1.3
+    * License: Creative Commons Attribution 3.0 License 
+    * Licence Path: https://creativecommons.org/licenses/by/3.0/
+    * Project: http://www.famfamfam.com/lab/icons/silk/
+    * Source:  http://www.famfamfam.com/lab/icons/silk/
+
+ods530.idl
+Permission of use:
+From Hans-Georg Swolana, Chairman of the Board ASAM e.V.,
+ Prof. Dr. Marcus Rieker, Member of the Board ASAM e.V.
+Date: Hoehenkirchen, 06/01/2016
+"The ASAM Board of Directors releases the IDL files for use under the EPL to the Eclipse IWG openMDM.
+This is valid for all versions of ASAM ODS 5.3.x.
+This permission is valid under the conditions of Eclipse will not modify the file."
+
+AvalonEvent.idl, CorbaFileServer.idl
+Permission of use:
+From Dr. Ralph Noerenberg
+Date: 08/15/2016
+"Herewith, we release the generated Client-Source-Code generated from our CORBA IDLs, namely
+* CORBANotification Service (generated from „AvalonEvent.idl”)
+* CORBAFileServer (generated from „CorbaFileServer.idl“),
+Under the Eclipse Public License (EPL). This agreement does not include the „AvalonEvent.idl“ and
+„CorbaFileServer.idl“ itself, which remain protected property of HighQSoft. "
+
+OMG Notification Service Specification Version: 1.1
+The terms of use are defined in section "Freely Available And Available
+Free" see: http://www.omg.org/gettingstarted/overview.htm
+
+OMG Event Service Specification Version: 1.2
+The terms of use are defined in section "Freely Available And Available
+Free" see: http://www.omg.org/gettingstarted/overview.htm
+
+
+## Cryptography
+
+Content may contain encryption software. The country in which you are currently
+may have restrictions on the import, possession, and use, and/or re-export to
+another country, of encryption software. BEFORE using any encryption software,
+please check the country's laws, regulations and policies concerning the import,
+possession, or use, and re-export of encryption software, to see if this is
+permitted.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4ecbc75
--- /dev/null
+++ b/README.md
@@ -0,0 +1,621 @@
+## minimum requirements
+* JDK 1.8.0_45
+* Gradle 2.13
+
+## build dependencies
+Before you can install and build the application, you have to checkout and install: (gradlew install)
+* org.eclipse.mdm.api.base
+* org.eclipse.mdm.api.default
+* org.eclipse.mdm.api.odsadapter
+
+## build, deploy and configure the application
+
+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-${version}.zip** at
+**/org.eclipse.mdm.nucleus/build/distributions**
+The ZIP archive contains the backend **org.eclipse.mdm.nucleus-${version}.war** and the configurations **/configuration**
+3. **check** that the database for the preference service is running or else start it with **asadmin start-database**.
+4. **deploy** the backend ( **org.eclipse.mdm.nucleuss-${version}.war** file) at your application server. Make sure to deploy the war file with application name **org.eclipse.mdm.nucleus**, otherwise the LoginRealmModule is not able to lookup the ConnectorService EJB. Additionally in the following examples, we assume that the context root is also set to **org.eclipse.mdm.nucleus**.
+When deploying on command line you can use: **asadmin deploy --name org.eclipse.mdm.nucleus "/path/to/org.eclipse.mdm.nucleus-${version}.war"**
+5. **copy the content** of the extracted **/configuration** folder to **GLASSFISH_ROOT/glassfish/domains/domain1/config**
+6. **edit** the **org.eclipse.mdm.connector/service.xml** file to configure the data sources
+7. **configure** a **LoginModule** with name **MDMRealm** (See section **Configure LoginModule** for details)
+8. **restart** the application server
+9. **visit** the main page of the client to make sure everything works fine. The main page of the client should be available under
+http://SERVER:PORT/{APPLICATIONROOT}
+_(eg: http://localhost:8080/org.eclipse.mdm.nucleus_)
+
+## configure LoginModule
+MDM 5 backend implements the delegated approach to roles and permissions, wherein the data sources (ASAM ODS server, PAK cloud)
+themselves can implement their own security scheme (which they already have) and then delegates the appropriate
+user data to the backends.
+
+In the case of ASAM ODS servers this is done using a technical user and the `for_user` attribute on the established
+connection, so it is a form of 'login on behalf'.
+
+In the case of PAK Cloud this is done by passing the user name along with a http header `X-Remote-User` which is then
+used by PAK Cloud to establish the users roles (from an internal database or an external authentication provider).
+
+Before the user is 'logged in on behalf' he is authenticated by a LoginModule within the Glassfish application server.
+There are different implementations available (e.g. LDAP, Certificate, JDBC, ...). To keep this guide simple, we will setup
+a FileRealm, which stores the user information in a flat file:
+
+The following command will create a FileRealm with name `MDMRealm` that stores the users in a file called `mdm-keyfile`:
+
+```
+asadmin create-auth-realm --classname com.sun.enterprise.security.auth.realm.file.FileRealm --property file=${com.sun.aas.instanceRoot}/config/mdm-keyfile:jaas-context=MDMRealm:assign-groups=MDM MDMRealm
+```
+
+To be able to login you need to explicitly add users to the `MDMRealm`:
+
+```
+asadmin create-file-user --authrealmname MDMRealm
+```
+
+Next you need to add the following snippet to your `login.conf` in `${com.sun.aas.instanceRoot}/config/`
+
+```
+MDMRealm {
+  com.sun.enterprise.security.auth.login.FileLoginModule required;
+};
+```
+As a last step you have to provide the credentials of the technical user in adapter configuration in `service.xml`. 
+For the ODS adapter you you have to specify the parameters `user` and `password` of the technical user. For example:
+
+```
+<service entityManagerFactoryClass="org.eclipse.mdm.api.odsadapter.ODSContextFactory">
+    ...
+	<param name="user">sa</param>
+	<param name="password">sa</param>
+	...
+</service>
+```
+
+Make sure to restart Glassfish after this change.
+
+### Remove MDMLoginRealm from previous versions:
+In versions 5.0.0M1, 0.10 and older the configuration of a custom login realm was neccessary. If you configured your glassfish instance
+for one of these version, you can remove the old configuration options and artifact, as they are no longer needed:
+
+* **delete** the jar file **org.eclipse.mdm.realm.login.glassfish-VERSION.jar** from **GLASSFISH4_ROOT/glassfish/domains/domain1/lib**
+   
+* **open** the Glassfish login **configuration file** at **GLASSFISH4_ROOT/glassfish/domains/domain1/config/login.conf**
+   
+* **delete** custom MDM realm module entry to this config file
+    
+```   
+MDMLoginRealm {
+  org.eclipse.mdm.realm.login.glassfish.LoginRealmModule required;
+};
+```
+
+* **remove** the MDMLoginRealm by executing the following command or by deleting it in the Glassfish web console:
+ 
+```
+asadmin delete-auth-realm MDMLoginRealm
+```
+
+
+## configure logging
+
+MDM 5 uses SLF4J and logback for logging. The default configuration file can be found at **org.eclipse.mdm.nucleus/src/main/resources/logback.xml**. It logs INFO level messages to **mdm5.log** in the **logs** folder of the Glassfish domain. If you want to customize logging, you can either edit the file within the war file or preferably provide your own logging configuration via system parameter in the JVM settings in Glassfish: **-Dlogback.configurationFile=/path/to/config.xml**
+
+## available rest URLs
+
+**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_
+
+Since all other calls operate on a specific source all subsequent calls share the common prefix
+`http://{SERVER}:{PORT}/{APPLICATIONROOT}/mdm/environments/{SOURCENAME}/`, which we will be our root for the description of the next calls.
+Strings enclosed in curly brackets are meant to be replaced by appropriate values. The following parameters are recurring throughout the different URLs:
+
+* APPLICATIONROOT is the context root under which MDM is deployed
+* SOURCENAME is the source name the underlying data source
+* CONTEXTTYPE is one of [unitundertest, testsequence, testequipment]
+* DATATYPE is one of [STRING, STRING_SEQUENCE, DATE, DATE_SEQUENCE, BOOLEAN, BOOLEAN_SEQUENCE, BYTE, BYTE_SEQUENCE, SHORT, SHORT_SEQUENCE, INTEGER, INTEGER_SEQUENCE, LONG, LONG_SEQUENCE, FLOAT, FLOAT_SEQUENCE, DOUBLE, DOUBLE_SEQUENCE, BYTE_STREAM, BYTE_STREAM_SEQUENCE, FLOAT_COMPLEX, FLOAT_COMPLEX_SEQUENCE, DOUBLE_COMPLEX, DOUBLE_COMPLEX_SEQUENCE, FILE_LINK, FILE_LINK_SEQUENCE] 
+* FILTERSTRING is a String defining a filter. For example `Test.Name eq 't*'` filters for all tests which names begin with `t`.
+
+
+**Business Object: Test**
+
+* GET:    /tests
+* GET:    /tests?filter={FILTERSTRING}
+* GET:    /tests/searchattributes
+* GET:    /tests/localizations
+* GET:    /tests/{TESTID}
+
+**Business Object: TestStep**
+
+* GET:    /teststeps
+* GET:    /teststeps?filter=FILTERSTRING
+* GET:    /teststeps/searchattributes
+* GET:    /teststeps/localizations
+* GET:    /teststeps/{TESTSTEPID}
+* GET:    /teststeps/{TESTSTEPID}/contexts
+* GET:    /teststeps/{TESTSTEPID}/contexts/{CONTEXTTYPE}
+
+**Business Object: Measurement**
+
+* GET:    /measurements
+* GET:    /measurements?filter={FILTERSTRING}
+* GET:    /measurements/searchattributes
+* GET:    /measurements/localizations
+* GET:    /measurements/{MEASUREMENTID}
+* GET:    /measurements/{MEASUREMENTID}/contexts
+* GET:    /measurements/{MEASUREMENTID}/contexts/{CONTEXTTYPE}
+
+**Business Object: ChannelGroup**
+
+* GET:    /channelgroups
+* GET:    /channelgroups/localizations
+* GET:    /channelgroups/{CHANNELGROUPID}
+
+**Business Object: Channel**
+
+* GET:    /channels
+* GET:    /channels/localizations
+* GET:    /channels/{CHANNELID}
+
+**Business Object: Project**
+
+* GET:    /projects
+* GET:    /projects?filter={FILTERSTRING}
+* GET:    /projects/searchattributes
+* GET:    /projects/localizations
+* GET:    /projects/{PROJECTID}
+
+**Business Object: Pool**
+
+* GET:    /pools
+* GET:    /pools?filter={FILTERSTRING}
+* GET:    /pools/searchattributes
+* GET:    /pools/localizations
+* GET:    /pools/{POOLID}
+
+**Business Object: ValueList**
+
+* GET:    /valuelists
+* POST:   /valuelists (JSON: { "name" : "testValueList" })
+* GET:    /valuelists/{VALUELISTID}
+* PUT:    /valuelists/{VALUELISTID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /valuelists/{VALUELISTID}
+* GET:    /valuelists/searchattributes
+* GET:    /valuelists/localizations
+
+**Business Object: ValueListValue**
+
+* GET:    /valuelists/{VALUELISTID}/valuelistvalues
+* POST:   /valuelists/{VALUELISTID}/valuelistvalues (JSON: { "name" : "testValueListValue" })
+* GET:    /valuelists/{VALUELISTID}/valuelistvalues/{VALUELISTVALUEID}
+* PUT:    /valuelists/{VALUELISTID}/valuelistvalues/{VALUELISTVALUEID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /valuelists/{VALUELISTID}/valuelistvalues/{VALUELISTVALUEID}
+* GET:    /valuelists/{VALUELISTID}/valuelistvalues/searchattributes
+* GET:    /valuelists/{VALUELISTID}/valuelistvalues/localizations
+
+**Business Object: PhysicalDimension**
+
+* GET:    /physicaldimensions
+* POST:   /physicaldimensions (JSON: { "name" : "testPhysicalDimension" })
+* GET:    /physicaldimensions/{PHYSICALDIMENSIONID}
+* PUT:    /physicaldimensions/{PHYSICALDIMENSIONID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /physicaldimensions/{PHYSICALDIMENSIONID}
+* GET:    /physicaldimensions/searchattributes
+* GET:    /physicaldimensions/localizations
+
+**Business Object: Unit**
+
+* GET:    /units
+* POST:   /units (JSON: { "name" : "testUnit", "physicaldimension" : "PHYSICALDIMENSIONID" })
+* GET:    /units/{UNITID}
+* PUT:    /units/{UNITID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /units/{UNITID}
+* GET:    /units/searchattributes
+* GET:    /units/localizations
+
+**Business Object: Quantity**
+
+* GET:    /quantities
+* POST:   /quantities (JSON: { "name" : "testQuantity", "unit" : "UNITID" })
+* GET:    /quantities/{QUANTITYID}
+* PUT:    /quantities/{QUANTITYID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /quantities/{QUANTITYID}
+* GET:    /quantities/searchattributes
+* GET:    /quantities/localizations
+
+**Business Object: CatalogComponent**
+
+* GET:    /catcomps/{CONTEXTTYPE}
+* POST:   /catcomps/{CONTEXTTYPE} (JSON: { "name" : "testCatalogComponent" })
+* GET:    /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID}
+* PUT:    /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID}
+* GET:    /catcomps/{CONTEXTTYPE}/searchattributes
+* GET:    /catcomps/{CONTEXTTYPE}/localizations
+
+**Business Object: CatalogAttribute**
+
+* GET:    /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID}/catattrs
+* POST:   /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID}/catattrs (JSON: { "name" : "testCatalogAttribute", "datatype" : "DATATYPE" })
+* GET:    /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID}/catattrs/{CATALOGATTRIBUTEID}
+* PUT:    /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID}/catattrs/{CATALOGATTRIBUTEID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID}/catattrs/{CATALOGATTRIBUTEID}
+* GET:    /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID}/catattrs/searchattributes
+* GET:    /catcomps/{CONTEXTTYPE}/{CATALOGCOMPONENTID}/catattrs/localizations
+
+**Business Object: CatalogSensor**
+
+* GET:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors
+* POST:   /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors (JSON: { "name" : "testCatalogSensor" })
+* GET:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID}
+* PUT:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID}
+* GET:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/searchattributes
+* GET:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/localizations
+
+**Business Object: CatalogSensorAttribute**
+
+* GET:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID}/catsensorattrs
+* POST:   /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID}/catsensorattrs (JSON: { "name" : "testCatalogAttribute", "datatype" : "DATATYPE" })
+* GET:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID}/catsensorattrs/{CATALOGATTRIBUTEID}
+* PUT:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID}/catsensorattrs/{CATALOGATTRIBUTEID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID}/catsensorattrs/{CATALOGATTRIBUTEID}
+* GET:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID}/catsensorattrs/searchattributes
+* GET:    /catcomps/testequipment/{CATALOGCOMPONENTID}/catsensors/{CATALOGSENSORID}/catsensorattrs/localizations
+
+**Business Object: TemplateRoot**
+
+* GET:    /tplroots/{CONTEXTTYPE}
+* POST:   /tplroots/{CONTEXTTYPE} (JSON: { "name" : "testTemplateRoot" })
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}
+* PUT:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}
+* GET:    /tplroots/{CONTEXTTYPE}/searchattributes
+* GET:    /tplroots/{CONTEXTTYPE}/localizations
+
+**Business Object: TemplateComponent**
+
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps
+* POST:   /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps (JSON: { "name" : "testTemplateComponent", "catalogcomponent" : "CATALOGCOMPONENTID" })
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}
+* PUT:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/searchattributes
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/localizations
+
+**Business Object: TemplateAttribute**
+
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplattrs
+* POST:   /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplattrs (JSON: { "name" : "testCatalogAttribute" } (name must be identical with corresponding CatalogAttribute))
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplattrs/{TEMPLATEATTRIBUTEID}
+* PUT:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplattrs/{TEMPLATEATTRIBUTEID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplattrs/{TEMPLATEATTRIBUTEID}
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplattrs/searchattributes
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplattrs/localizations
+
+**Business Object: TemplateSensor**
+
+* GET:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors
+* POST:   /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors (JSON: { "name" : "testTemplateSensor", "catalogsensor" : "CATALOGSENSORID", "quantity" : "QUANTITYID" })
+* GET:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/{TEMPLATESENSORID}
+* PUT:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/{TEMPLATESENSORID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/{TEMPLATESENSORID}
+* GET:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/searchattributes
+* GET:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/localizations
+
+**Business Object: TemplateSensorAttribute**
+
+* GET:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/{TEMPLATESENSORID}/tplsensorattrs
+* GET:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/{TEMPLATESENSORID}/tplsensorattrs/{TEMPLATEATTRIBUTEID}
+* PUT:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/{TEMPLATESENSORID}/tplsensorattrs/{TEMPLATEATTRIBUTEID} (JSON: { "MimeType" : "myMimeType" })
+* GET:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/{TEMPLATESENSORID}/tplsensorattrs/searchattributes
+* GET:    /tplroots/testequipment/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplsensors/{TEMPLATESENSORID}/tplsensorattrs/localizations
+
+**Business Object: NestedTemplateComponent**
+
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps
+* POST:   /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps (JSON: { "name" : "testNestedTemplateComponent", "catalogcomponent" : "CATALOGCOMPONENTID" })
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}
+* PUT:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/searchattributes
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/localizations
+
+**Business Object: NestedTemplateAttribute**
+
+* CONTEXTTYPE is one of [unitundertest, testsequence, testequipment]
+
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs
+* POST:   /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs (JSON: { "name" : "testCatalogAttribute" } (name must be identical with corresponding CatalogAttribute))
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/{NESTEDTEMPLATEATTRIBUTEID}
+* PUT:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/{NESTEDTEMPLATEATTRIBUTEID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/{NESTEDTEMPLATEATTRIBUTEID}
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/searchattributes
+* GET:    /tplroots/{CONTEXTTYPE}/{TEMPLATEROOTID}/tplcomps/{TEMPLATECOMPONENTID}/tplcomps/{NESTEDTEMPLATECOMPONENTID}/tplattrs/localizations
+
+**Business Object: TemplateTest**
+
+* GET:    /tpltests
+* POST:   /tpltests (JSON: { "name" : "testTemplateTest" })
+* GET:    /tpltests/{TEMPLATETESTID}
+* PUT:    /tpltests/{TEMPLATETESTID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /tpltests/{TEMPLATETESTID}
+* GET:    /tpltests/searchattributes
+* GET:    /tpltests/localizations
+
+**Business Object: TemplateTestStep**
+
+* GET:    /tplteststeps
+* POST:   /tplteststeps (JSON: { "name" : "testTemplateTestStep" })
+* GET:    /tplteststeps/{TEMPLATETESTSTEPID}
+* PUT:    /tplteststeps/{TEMPLATETESTSTEPID} (JSON: { "MimeType" : "myMimeType" })
+* DELETE: /tplteststeps/{TEMPLATETESTSTEPID}
+* GET:    /tplteststeps/searchattributes
+* GET:    /tplteststeps/localizations
+
+**Business Object: TemplateTestStepUsage**
+
+* GET:    /tpltests/{TEMPLATETESTID}/tplteststepusages
+* POST:   /tpltests/{TEMPLATETESTID}/tplteststepusages (JSON: { "name" : "testTemplateTestStepUsage", "tplteststep" : "TEMPLATETESTSTEPID" })
+* GET:    /tpltests/{TEMPLATETESTID}/tplteststepusages/{TEMPLATETESTSTEPUSAGEID}
+* DELETE: /tpltests/{TEMPLATETESTID}/tplteststepusages/{TEMPLATETESTSTEPUSAGEID}
+* GET:    /tpltests/{TEMPLATETESTID}/tplteststepusages/searchattributes
+* GET:    /tpltests/{TEMPLATETESTID}/tplteststepusages/localizations
+
+**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
+2. **edit** the configuration (global.properties) to fit your environment. You need an ODS Server which supports Notifications. All fields have to be there, but can be empty. However certain ODS Servers ignore some parameters (e.g. PeakODS ignores pollingIntervall since it pushes notifications).
+3. **start up** the application. At the first run it will index the database. This might take a while. After that MDM registers itself as NotificationListener and adapts all changes one-by-one.
+
+### 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
+  - Shopping basket file extensions
+
+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}"
+        }
+      }
+    }
+    
+2.) Shopping basket file extensions
+
+When downloading the contents of a shopping basket, a file with extension `mdm` is generated. The file extension can be changed by adding a preference with key `shoppingbasket.fileextensions`. For example the used extension can be set to `mdm-xml` by setting the value to `{ "default": "mdm-xml" }`.
+
+### 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>
+***
+
+## Copyright and License ##
+Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+
+ See the NOTICE file(s) distributed with this work for additional
+ information regarding copyright ownership.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v. 2.0 which is available at
+ http://www.eclipse.org/legal/epl-2.0.
+
+ SPDX-License-Identifier: EPL-2.0
\ No newline at end of file
diff --git a/WebContent/META-INF/MANIFEST.MF b/WebContent/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5e94951
--- /dev/null
+++ b/WebContent/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0

+Class-Path: 

+

diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..cfa531b
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,148 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+ 
+group = 'org.eclipse.mdm'
+version = '5.0.0'
+
+description = 'mdm nucleus'
+apply plugin: 'war'
+apply plugin: 'maven'
+apply plugin: 'eclipse'
+apply plugin: 'jacoco'
+apply plugin: 'org.sonarqube'
+
+sourceCompatibility = 1.8
+
+repositories {
+	jcenter()
+	mavenLocal()
+	mavenCentral()
+}
+
+subprojects {
+	group = rootProject.group
+	version = rootProject.version
+	
+	apply plugin: 'eclipse'
+	apply plugin: 'java'
+	apply plugin: 'maven'
+	apply plugin: 'jacoco'
+
+	sourceCompatibility = 1.8
+
+	repositories {
+		jcenter()
+		mavenLocal()
+		mavenCentral()
+	}
+
+	dependencies {
+		compileOnly 'org.slf4j:slf4j-api:1.7.25'
+		compileOnly 'javax:javaee-api:7.0'
+		runtime 'ch.qos.logback:logback-classic:1.2.3'
+
+		testCompile 'junit:junit:4.12'
+		testCompile 'org.assertj:assertj-core:3.6.2'
+		testCompile 'org.mockito:mockito-core:2.13.0'
+		testCompile 'javax:javaee-api:7.0'
+	}
+}
+
+clean {
+	dependsOn gradle.includedBuilds*.task(':clean')
+}
+
+configurations.all {
+	exclude group: 'com.sun.mail', module: 'javax.mail'
+	exclude group: 'javax.ws.rs', module: 'javax.ws.rs-api'
+	exclude group: 'javax.activation', module: 'activation'
+	exclude group: 'javax.annotation', module: 'javax.annotation-api'
+	exclude group: 'javax.inject', module: 'javax.inject'
+	exclude group: 'org.glassfish.hk2.external', module: 'javax.inject'
+	exclude group: 'commons-logging', module: 'commons-logging'
+	// exclude guava dependencies
+	exclude group: 'com.google.code.findbugs', module: 'jsr305'
+	exclude group: 'com.google.errorprone', module: 'error_prone_annotations'
+	exclude group: 'com.google.j2objc', module: 'j2objc-annotations'
+	exclude group: 'org.codehaus.mojo', module: 'animal-sniffer-annotations'
+	exclude group: 'org.checkerframework', module: 'checker-compat-qual'
+}
+
+dependencies { compile project(':org.eclipse.mdm.application') }
+
+
+task collectConfiguration() {
+	doLast {
+		subprojects.each { subproject ->
+			copy {
+				from "${project.projectDir}/${subproject.name}/src/main/configuration/"
+				into "${project.projectDir}/build/tmp/mdmweb/configuration/${subproject.name}"
+			}
+		}
+	}
+}
+
+task copySchema(dependsOn: ':org.eclipse.mdm.preferences:generateSchema') {
+	doLast {
+		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-${version}.zip"
+	from "${project.projectDir}/build/tmp/mdmweb"
+	from "${project.projectDir}/build/libs/"
+}
+distribute.dependsOn(collectConfiguration)

+distribute.dependsOn(copySchema)

+
+war {
+	from ('org.eclipse.mdm.application/build/node/dist') {
+		include('assets/*')
+		include('*')
+	}
+
+	webXml = file('org.eclipse.mdm.application/src/main/webconfig/web.xml')
+	webInf  {from 'org.eclipse.mdm.application/src/main/webconfig/glassfish-web.xml'}
+	
+	metaInf { from 'NOTICE.txt' }
+	metaInf { from 'LICENSE.txt' }
+}
+
+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.5' }
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..7a3265e
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..b82e006
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME

+distributionPath=wrapper/dists

+zipStoreBase=GRADLE_USER_HOME

+zipStorePath=wrapper/dists

+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip

diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS=

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windows variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/org.eclipse.mdm.application/build.gradle b/org.eclipse.mdm.application/build.gradle
new file mode 100644
index 0000000..90dc16e
--- /dev/null
+++ b/org.eclipse.mdm.application/build.gradle
@@ -0,0 +1,136 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+ 
+
+import org.apache.tools.ant.taskdefs.condition.Os
+import org.apache.tools.ant.filters.ConcatFilter
+
+description = 'MDM Web Application'
+
+apply plugin: 'com.moowork.node'
+
+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.23.2'
+	compile 'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.9.2'
+
+	compile project(':org.eclipse.mdm.businessobjects')
+	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 {
+	repositories {
+		maven {  url "https://plugins.gradle.org/m2/"  }
+	}
+	dependencies {  classpath "com.moowork.gradle:gradle-node-plugin:1.0.1"  }
+}
+
+jar {
+	metaInf { from '../NOTICE.txt' }
+	metaInf { from '../LICENSE.txt' }
+}
+
+task copy_websources(type: Copy) {
+
+	from('./src/main/webapp') {
+		exclude 'dist'
+		exclude 'node_modules'
+		exclude 'NOTICE.html'
+	}
+
+	into "${project.buildDir}/node"
+}
+
+task copyNoticeFile() {
+  file("${project.buildDir}/node/src/app/").mkdirs()
+  def html = file("${project.buildDir}/node/src/app/NOTICE.html")
+  html.text = '<pre>' + file('../NOTICE.txt').text + '</pre>'
+}
+
+
+node {
+	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 runBuild(type: NpmTask) {
+	args = ['run', 'ci_build']
+	inputs.dir 'src'
+	outputs.dir 'build'
+}
+
+task copy_loginsites(type: Copy) {
+	from 	"${project.buildDir}/node/src/login.jsp",
+			"${project.buildDir}/node/src/login.css",
+			"${project.buildDir}/node/src/error.jsp"
+
+	into	 "${project.buildDir}/node/dist"
+}
+
+task copyFilesForRunningWithoutNpm(type: Copy){
+	from	"${project.buildDir}/node/src/index.html"
+	into	"${project.buildDir}/node/dist"
+}
+
+if (hasProperty('skipNode')) {
+  compileJava.dependsOn(copy_loginsites)
+} else {
+	compileJava.dependsOn(copy_loginsites)
+	map_tslint.dependsOn(runBuild)
+	copy_loginsites.dependsOn(runBuild)
+	runBuild.dependsOn(npmInstall)
+	npmInstall.dependsOn(copy_websources)
+	npmInstall.dependsOn(copyNoticeFile)
+	copy_websources.dependsOn(npmSetup)
+	npmSetup.dependsOn(nodeSetup)
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/AccessControlResponseFilter.java b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/AccessControlResponseFilter.java
new file mode 100644
index 0000000..c9b61fe
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/AccessControlResponseFilter.java
@@ -0,0 +1,46 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.application;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.ext.Provider;
+
+@Provider
+@Priority(Priorities.HEADER_DECORATOR)
+public class AccessControlResponseFilter implements ContainerResponseFilter {
+
+	@Override
+	public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
+
+		final MultivaluedMap<String, Object> responseHeaders = responseContext.getHeaders();
+		final MultivaluedMap<String, String> requestHeaders = requestContext.getHeaders();
+
+		responseHeaders.add("Access-Control-Allow-Origin", "*");
+		responseHeaders.add("Access-Control-Allow-Headers", "Authorization, Origin, X-Requested-With, Content-Type");
+		responseHeaders.add("Access-Control-Expose-Headers", "Location, Content-Disposition");
+		responseHeaders.add("Access-Control-Allow-Methods", "POST, PUT, GET, DELETE, HEAD, OPTIONS");
+
+		requestHeaders.add("Access-Control-Allow-Origin", "*");
+		requestHeaders.add("Access-Control-Allow-Headers", "Authorization, Origin, X-Requested-With, Content-Type");
+		requestHeaders.add("Access-Control-Expose-Headers", "Location, Content-Disposition");
+		requestHeaders.add("Access-Control-Allow-Methods", "POST, PUT, GET, DELETE, HEAD, OPTIONS");
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/ApplicationConfig.java b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/ApplicationConfig.java
new file mode 100644
index 0000000..c7472c1
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/ApplicationConfig.java
@@ -0,0 +1,32 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.application;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+/**
+ * Application configuration
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@ApplicationPath("mdm")
+public class ApplicationConfig extends Application {
+
+	public ApplicationConfig() {
+	}
+}
diff --git a/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/MDMRequestFilter.java b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/MDMRequestFilter.java
new file mode 100644
index 0000000..99ddfb7
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/MDMRequestFilter.java
@@ -0,0 +1,67 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.application;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * MDMRequestFilter
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class MDMRequestFilter implements Filter {
+
+	private static final String SERVLET_NAME_MDMNENUE = "/navigator/";
+
+
+	@Override
+	public void init(FilterConfig filterConfig) throws ServletException {
+
+	}
+
+	@Override
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+			throws IOException, ServletException {
+		if (request instanceof HttpServletRequest) {
+			HttpServletRequest httpRequest = (HttpServletRequest) request;
+			String requestedURL = httpRequest.getRequestURI().toLowerCase();
+
+			if (requestedURL.trim().contains(SERVLET_NAME_MDMNENUE)) {
+				if (response instanceof HttpServletResponse) {
+					String location = httpRequest.getContextPath();
+					((HttpServletResponse) response).sendRedirect(location);
+				}
+			} else {
+				chain.doFilter(request, response);
+			}
+		}
+	}
+
+	@Override
+	public void destroy() {
+	}
+
+}
diff --git a/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/logout/MDMLogoutServlet.java b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/logout/MDMLogoutServlet.java
new file mode 100644
index 0000000..2fdc6d9
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/java/org/eclipse/mdm/application/logout/MDMLogoutServlet.java
@@ -0,0 +1,44 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.application.logout;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * MDMLogoutServlet
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@WebServlet(name = "MDMLogoutServlet", urlPatterns = { "/mdm/logout" })
+public class MDMLogoutServlet extends HttpServlet {
+
+	private static final long serialVersionUID = -2243639870075761399L;
+
+	@Override
+	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+		req.logout();
+		req.getSession().invalidate();
+		resp.sendRedirect(req.getContextPath());
+	}
+
+}
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..f81c782
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/README.md
@@ -0,0 +1,43 @@
+# 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).
+
+## Copyright and License ##
+Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+
+ See the NOTICE file(s) distributed with this work for additional
+ information regarding copyright ownership.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v. 2.0 which is available at
+ http://www.eclipse.org/legal/epl-2.0.
+
+ SPDX-License-Identifier: EPL-2.0
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/e2e/app.e2e-spec.ts b/org.eclipse.mdm.application/src/main/webapp/e2e/app.e2e-spec.ts
new file mode 100644
index 0000000..ad51239
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/e2e/app.e2e-spec.ts
@@ -0,0 +1,28 @@
+/********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+ 
+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..d80758f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/e2e/app.po.ts
@@ -0,0 +1,25 @@
+/********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+ 
+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/karma.conf.js b/org.eclipse.mdm.application/src/main/webapp/karma.conf.js
new file mode 100644
index 0000000..38547aa
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/karma.conf.js
@@ -0,0 +1,62 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+// 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/package.json b/org.eclipse.mdm.application/src/main/webapp/package.json
new file mode 100644
index 0000000..43cb68b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/package.json
@@ -0,0 +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": {
+    "ng": "ng",
+    "start": "ng serve --proxy proxies/development.config.json",
+    "build": "ng build --prod",
+    "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 --prod --base-href /org.eclipse.mdm.nucleus/"
+  },
+  "private": true,
+  "dependencies": {
+    "@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",
+    "ngx-bootstrap": "1.9.1",
+    "ng2-split-pane": "1.3.1",
+    "primeng": "2.0.5",
+    "reflect-metadata": "0.1.3",
+    "rxjs": "5.1.0",
+    "ts-helpers": "1.1.1",
+    "zone.js": "0.7.6"
+  },
+  "devDependencies": {
+    "@angular-devkit/schematics":"0.0.34",
+    "@angular-devkit/core": "0.0.22",
+    "@angular/cli": "1.4.7",
+    "@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..e73bc0c
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/protractor.conf.js
@@ -0,0 +1,45 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+// 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/NOTICE.html b/org.eclipse.mdm.application/src/main/webapp/src/app/NOTICE.html
new file mode 100644
index 0000000..02711ad
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/NOTICE.html
@@ -0,0 +1,337 @@
+<pre>

+# Notices for Eclipse MDM|BL

+

+This content is produced and maintained by the Eclipse MDM|BL project,

+it is a project of the openMDM(R) Eclipse Working Group.

+

+* Project home: https://projects.eclipse.org/projects/technology.mdmbl

+

+## Trademarks

+

+Eclipse MDM|BL, MDM|BL and Eclipse openMDM(R) logo are registered trademarks

+of the Eclipse Foundation.

+

+## Copyright

+

+All content is the property of the following respective authors or their employers.

+For more information regarding authorship of content, please consult the listed

+source code repository logs.

+

+Copyright (c) 2016-2018 Gigatronik Ingolstadt GmbH

+Copyright (c) 2016-2018 Peak Solution GmbH

+Copyright (c) 2017-2018 science + computing AG Tuebingen (ATOS SE)

+Copyright (c) 2017-2018 Canoo Engineering AG

+Copyright (c) 2017 Florian Schmitt

+Copyright (c) 2017-2018 Angelika Wittek

+Copyright (c) 2018 Elektronische Fahrwerksysteme GMBH

+Copyright (c) 2018 Karakun AG

+

+## Declared Project Licenses

+

+This program and the accompanying materials are made available under the

+terms of the Eclipse Public License v. 2.0 which is available at

+http://www.eclipse.org/legal/epl-2.0.

+

+SPDX-License-Identifier: EPL-2.0

+

+## Source Code

+

+The project maintains the following source code repositories:

+

+org.eclipse.mdm.api.base.git          - The openMDM(R) API.

+org.eclipse.mdm.api.default.git     - Extension of the openMDM(R) API containing default elements.

+org.eclipse.mdm.api.odsadapter.git  - ODS implementation of persistence adapter.

+org.eclipse.mdm.nucleus.git         - Core building blocks for the openMDM Business Logic and Web Frontend.

+

+## Third-party Content

+

+The Content includes items that have been sourced from third parties as set out below.

+If you did not receive this Content directly from the Eclipse Foundation, the following

+is provided for informational purposes only, and you should look to

+the Redistributor's license for terms and conditions of use.

+

+antlr4-4.5.3.jar(2.5.3)

+    * License: New BSD license

+

+aopalliance-repackaged-2.5.0-b05.jar (2.5.0-b05)

+    * License: CDDL

+

+commons-codec-1.2.jar (1.2)

+    * License: Apache License, 2.0

+

+commons-httpclient-3.1.jar  (3.1)

+    * License: Apache License, 2.0

+

+commons-lang3-3.4.jar (3.4)

+    * License: Apache License, 2.0

+

+gson-2.7.jar (2.7)

+    * License: Apache License, 2.0

+

+Google Guava Version: 25.0-jre  (25.0)

+    * License: Apache License, 2.0

+    

+Gradle Wrapper (4.10.2)

+    * License: Apache License, 2.0     

+

+hk2-api-2.5.0-b05.jar (2.5.0-b05)

+    * License: CDDL-1.1

+

+hk2-locator-2.5.0-b05.jar(2.5.0-b05)

+    * License: CDDL

+

+hk2-utils-2.5.0-b05.jar  (2.5.0-b05)

+    * License: CDDL

+

+jackson-annotations-2.9.0.jar  (2.9.0)

+    * License: Apache License, 2.0

+

+jackson-core-2.9.2.jar(2.9.2)

+    * License: Apache License, 2.0

+

+jackson-databind-2.9.2.jar  (2.9.2)

+    * License: Apache License, 2.0

+

+jackson-jaxrs-base-2.9.2.jar(2.9.2)

+    * License: Apache License, 2.0

+

+jackson-jaxrs-json-provider-2.9.2.jar(2.9.2)

+    * License: Apache License, 2.0

+

+jackson-module-jaxb-annotations-2.9.2.jar  (2.9.2)

+    * License: Apache License, 2.0

+

+javassist-3.20.0-GA.jar  (2.20.0-GA)

+    * License: Apache 2.0

+

+jcl-over-slf4j-1.7.25.jar(1.7.25)

+    * License: MIT License

+    * Licence Path: https://www.slf4j.org/license.html

+    * Project URL:  https://www.slf4j.org

+    * Source URL:   https://github.com/qos-ch/slf4j

+

+jersey-client-2.23.2.jar (2.23.2)

+     * License: CDDL

+

+jersey-common-2.23.2.jar (2.23.2)

+    * License: CDDL

+

+jersey-container-servlet-2.23.2.jar  (2.23.2)

+    * License: CDDL

+

+jersey-container-servlet-core-2.23.3.jar(2.23.2)

+    * License: CDDL

+

+jersey-guava-2.23.2.jar  (2.23.2)

+    * License: Apache License, 2.0

+

+jersey-media-jaxb-2.23.2.jar(2.23.2)

+    * License: CDDL

+

+jersey-media-sse-2.23.2.jar (2.23.2)

+    * License: CDDL

+

+jersey-server-2.23.2.jar (2.23.2)

+    * License: Apache-2.0

+

+logback-classic-1.2.3.jar(1.2.3)

+    * License: Eclipse Public License 1.0

+

+logback-core-1.2.3.jar(1.2.3)

+    * License: Eclipse Public License 1.0

+

+osgi-resource-locator-1.0.1.jar(1.0.1)

+    * License: CDDL

+

+protobuf-java-3.2.0.jar (3.2.0)

+    * License: New BSD license

+

+protobuf-java-util-3.2.0.jar  (3.2.0)

+    * License: New BSD license

+

+slf4j-api-1.7.25.jar  (1.7.25)

+    * License: MIT license

+    * Licence Path: https://www.slf4j.org/license.html

+    * Project URL: https://github.com/qos-ch/slf4j

+    * Source URL:  https://github.com/qos-ch/slf4j/releases/tag/v_1.7.25

+

+validation-api-1.1.0.Final.jar (1.1.0.Final)

+* License: Apache License, 2.0

+

+vavr-0.9.1-sources.jar (0.9.1)

+* License: Apache License, 2.0

+

+@angular/common@2.4.8

+    * License: MIT

+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE

+    * Project: https://github.com/angular/angular

+    * Source:  https://github.com/angular/angular

+

+@angular/compiler@2.4.8

+    * License: MIT

+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE

+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE

+    * Project: https://github.com/angular/angular

+    * Source:  https://github.com/angular/angular

+

+@angular/core@2.4.8

+    * License: MIT

+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE

+    * Project: https://github.com/angular/angular

+    * Source:  https://github.com/angular/angular

+

+@angular/forms@2.4.8

+    * License: MIT

+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE

+    * Project: https://github.com/angular/angular

+    * Source:  https://github.com/angular/angular

+

+@angular/http@2.4.8

+    * License: MIT

+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE

+    * Project: https://github.com/angular/angular

+    * Source:  https://github.com/angular/angular

+

+@angular/platform-browser-dynamic@2.4.8

+    * License: MIT

+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE

+    * Project: https://github.com/angular/angular

+    * Source:  https://github.com/angular/angular

+

+@angular/platform-browser@2.4.8

+    * License: MIT

+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE

+    * Project: https://github.com/angular/angular

+    * Source:  https://github.com/angular/angular

+

+@angular/router@3.4.8

+    * License: MIT

+    * Licence Path: https://github.com/angular/angular/blob/master/LICENSE

+    * Project: https://github.com/angular/angular

+    * Source:  https://github.com/angular/angular

+

+@types/file-saver@0.0.0

+    * License: MIT

+    * Licence Path: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/LICENSE

+    * Project: https://github.com/DefinitelyTyped/DefinitelyTyped

+    * Source:  https://github.com/DefinitelyTyped/DefinitelyTyped

+

+bootstrap@3.3.7

+    * License: MIT

+    * Licence Path: https://github.com/twbs/bootstrap/raw/master/LICENSE

+    * Project: https://github.com/twbs/bootstrap

+    * Source:  https://github.com/twbs/bootstrap

+

+class-transformer@0.1.6

+    * License: MIT

+    * Licence Path: https://github.com/typestack/class-transformer/blob/master/LICENSE

+    * Project: https://github.com/pleerock/class-transformer

+    * Source:  https://github.com/pleerock/class-transformer

+

+core-js@2.4.1

+    * License: MIT

+    * Licence Path: https://github.com/zloirock/core-js/raw/master/LICENSE

+    * Project: https://github.com/zloirock/core-js

+    * Source:  https://github.com/zloirock/core-js

+

+file-saver@1.3.3

+    * License: MIT

+    * Licence Path: https://github.com/eligrey/FileSaver.js/raw/master/LICENSE.md

+    * Project: https://github.com/eligrey/FileSaver.js

+    * Source:  https://github.com/eligrey/FileSaver.js

+

+font-awesome@4.7.0

+    * License: MIT

+    * Licence Path: https://github.com/FortAwesome/Font-Awesome/blob/master/LICENSE.txt

+    * Project: https://fontawesome.com/

+    * Source:  https://github.com/FortAwesome/Font-Awesome

+

+moment@2.17.1

+    * License: MIT

+    * Licence Path: https://github.com/moment/moment/raw/master/LICENSE

+    * Project: https://github.com/moment/moment

+    * Source:  https://github.com/moment/moment

+

+ngx-bootstrap@1.9.1

+    * License: MIT

+    * Licence Path: https://github.com/valor-software/ngx-bootstrap/blob/development/LICENSE

+    * Project: https://valor-software.com/ngx-bootstrap

+    * Source:  https://github.com/valor-software/ngx-bootstrap

+

+ng2-split-pane@1.3.1

+    * License: MIT

+    * Licence Path: https://github.com/wannabegeek/ng2-split-pane/raw/master/LICENSE

+    * Project: https://github.com/wannabegeek/ng2-split-pane

+    * Source:  https://github.com/wannabegeek/ng2-split-pane

+

+primeng@2.0.5

+    * License: MIT

+    * Licence Path: https://github.com/primefaces/primeng/raw/master/LICENSE.md

+    * Project: https://github.com/primefaces/primeng

+    * Source:  https://github.com/primefaces/primeng

+

+reflect-metadata@0.1.3

+    * License: Apache-2.0

+    * Project: https://github.com/rbuckton/ReflectDecorators

+    * Source:  https://github.com/rbuckton/ReflectDecorators

+

+rxjs@5.1.0

+    * License: Apache-2.0

+    * Project: https://github.com/ReactiveX/RxJS

+    * Source:  https://github.com/ReactiveX/RxJS

+

+symbol-observable@1.2.0

+    * License: MIT

+    * Licence Path: https://github.com/blesh/symbol-observable/raw/master/license

+    * Project: https://github.com/blesh/symbol-observable

+    * Source:  https://github.com/blesh/symbol-observable

+

+ts-helpers@1.1.1

+    * License: MIT

+    * Licence Path: https://github.com/ngParty/ts-helpers/raw/master/LICENSE

+    * Project: https://github.com/ngParty/ts-helpers

+    * Source:  https://github.com/ngParty/ts-helpers

+

+zone.js@0.7.6

+    * License: MIT

+    * Licence Path: https://github.com/angular/zone.js/raw/master/LICENSE

+    * Project: https://github.com/angular

+    * Source:  https://github.com/angular/zone.js

+    

+FFAMFAMFAM Silk Icon, Version 1.3

+    * License: Creative Commons Attribution 3.0 License 

+    * Licence Path: https://creativecommons.org/licenses/by/3.0/

+    * Project: http://www.famfamfam.com/lab/icons/silk/

+    * Source:  http://www.famfamfam.com/lab/icons/silk/

+

+ods530.idl

+Permission of use:

+"The ASAM Board of Directors releases the IDL files for use under the EPL to the Eclipse IWG openMDM.

+This is valid for all versions of ASAM ODS 5.3.x.

+This permission is valid under the conditions of Eclipse will not modify the file."

+

+AvalonEvent.idl, CorbaFileServer.idl

+"Herewith, we release the generated Client-Source-Code generated from our CORBA IDLs, namely

+* CORBANotification Service (generated from „AvalonEvent.idl”)

+* CORBAFileServer (generated from „CorbaFileServer.idl“),

+Under the Eclipse Public License (EPL). This agreement does not include the „AvalonEvent.idl“ and

+„CorbaFileServer.idl“ itself, which remain protected property of HighQSoft. "

+

+OMG Notification Service Specification Version: 1.1

+The terms of use are defined in section "Freely Available And Available

+Free" see: http://www.omg.org/gettingstarted/overview.htm

+

+OMG Event Service Specification Version: 1.2

+The terms of use are defined in section "Freely Available And Available

+Free" see: http://www.omg.org/gettingstarted/overview.htm

+

+

+## Cryptography

+

+Content may contain encryption software. The country in which you are currently

+may have restrictions on the import, possession, and use, and/or re-export to

+another country, of encryption software. BEFORE using any encryption software,

+please check the country's laws, regulations and policies concerning the import,

+possession, or use, and re-export of encryption software, to see if this is

+permitted.

+</pre>

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..8099510
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-modules.component.html
@@ -0,0 +1,33 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..7c1a92a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-modules.component.ts
@@ -0,0 +1,33 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..8d20ffd
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin-routing.module.ts
@@ -0,0 +1,40 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..a73d1c1
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/admin.module.ts
@@ -0,0 +1,48 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

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

+import { ComponentLoaderFactory } from 'ngx-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..2b4477f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.css
@@ -0,0 +1,32 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+@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..c034269
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.html
@@ -0,0 +1,68 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..d237fea
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/edit-preference.component.ts
@@ -0,0 +1,120 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+import { Component, Input, Output, ViewChild, EventEmitter, OnInit } from '@angular/core';
+import { FormGroup, FormControl, FormBuilder, FormArray, Validators } from '@angular/forms';
+
+import { ModalDirective } from 'ngx-bootstrap';
+
+import { PreferenceService, Preference, Scope } from '../core/preference.service';
+import { NodeService } from '../navigator/node.service';
+import { Node } from '../navigator/node';
+
+import {MDMNotificationService} from '../core/mdm-notification.service';
+
+@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[];
+
+    @ViewChild( 'lgModal' ) public childModal: ModalDirective;
+
+    constructor( private formBuilder: FormBuilder,
+                 private nodeService: NodeService,
+                 private notificationService: MDMNotificationService ) { }
+
+    ngOnInit() {
+        let node: Node;
+        this.nodeService.getNodes(node).subscribe(
+                env => this.envs = env,
+                error => this.notificationService.notifyError('Datenquelle kann nicht geladen werden.', 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..58c2822
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.css
@@ -0,0 +1,41 @@
+@charset "ISO-8859-1";

+

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

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+.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..493a730
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.html
@@ -0,0 +1,50 @@
+<!-- ********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..ab256fd
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/administration/preference.component.ts
@@ -0,0 +1,109 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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';
+
+import {MDMNotificationService} from '../core/mdm-notification.service';
+
+@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 )
+  editPreferenceComponent: EditPreferenceComponent;
+
+  constructor( private formBuilder: FormBuilder,
+               private preferenceService: PreferenceService,
+               private route: ActivatedRoute,
+               private notificationService: MDMNotificationService) { }
+
+  ngOnInit() {
+      this.sub = this.route.params.subscribe(
+        params => this.onScopeChange(params),
+        error => this.notificationService.notifyError('Geltungsbereich kann nicht geladen werden.', error)
+      );
+  }
+
+  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 ),
+            error => this.notificationService.notifyError('Einstellung kann nicht gespeichert werden.', error)
+          );
+      this.subscription.unsubscribe();
+  }
+
+  reloadPreference( preference: Preference ) {
+      this.preferenceService.getPreferenceForScope( preference.scope, preference.key )
+          .subscribe(
+            p => this.preferences.push(p[0]),
+            error => this.notificationService.notifyError('Einstellung kann nicht aktualisiert werden.', error)
+          );
+  }
+
+  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..6663aeb
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/app-routing.module.ts
@@ -0,0 +1,36 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..7cccb48
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/app.component.html
@@ -0,0 +1,38 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+<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 [routerLink]="" (click)="showAboutDialog()">{{TtlAbout}}</a></li>

+        <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>

+

+<p-dialog header="About" [(visible)]="displayAboutDialog" modal="true" dismissableMask="true" responsive="true">

+  <notice></notice>

+</p-dialog>

+

+<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..3896dc9
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/app.component.ts
@@ -0,0 +1,37 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

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

+

+@Component({

+  selector: 'app-root',

+  templateUrl: './app.component.html'

+})

+export class AppComponent {

+

+  readonly TtlLogout = 'Logout';

+  readonly TtlAbout = 'About';

+

+  links = [

+      { name: 'openMDM5 Web', path: '/navigator' },

+      { name: 'Administration', path: '/administration' }

+  ];

+  displayAboutDialog: boolean = false;

+

+  showAboutDialog() {

+    this.displayAboutDialog = true;

+  }

+}

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..4fafde8
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/app.module.ts
@@ -0,0 +1,76 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

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

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

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

+

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

+import { NoticeComponent } from './notice.component';

+

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

+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';

+import { MDMErrorHandler } from './core/mdm-error-handler';

+import { HttpErrorHandler } from './core/http-error-handler';

+

+@NgModule({

+  imports: [

+    BrowserModule,

+    HttpModule,

+    FormsModule,

+    AppRoutingModule,

+    MDMCoreModule,

+    MDMNavigatorViewModule,

+    AdminModule,

+    DialogModule

+  ],

+  declarations: [

+    AppComponent,

+    NoticeComponent

+  ],

+  providers: [

+    NodeService,

+    LocalizationService,

+    FilereleaseService,

+    BasketService,

+    NavigatorService,

+    QueryService,

+    NodeproviderService,

+    MDMNotificationService,

+    ViewService,

+    HttpErrorHandler,

+    { provide: ErrorHandler, useClass: MDMErrorHandler }

+  ],

+  bootstrap: [AppComponent]

+})

+export class AppModule {

+

+}

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

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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 '../core/preference.service';

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

+import {HttpErrorHandler} from '../core/http-error-handler';

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

+

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

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        BasketService,

+        PropertyService,

+        PreferenceService,

+        MockBackend,

+        BaseRequestOptions,

+        HttpErrorHandler,

+        {

+          provide: Http,

+          useFactory: (mockBackend, options) => {

+            return new Http(mockBackend, options);

+          },

+          deps: [MockBackend, BaseRequestOptions]

+        }

+      ]

+    });

+  });

+

+

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

+    it('should return value configured in preference', async(inject([BasketService, MockBackend], (basketService, mockBackend) => {

+

+      mockBackend.connections.subscribe(conn => {

+

+        let mockResponse = {

+          preferences: [

+          {

+            id: 2,

+            key: 'shoppingbasket.fileextensions',

+            scope: Scope.SYSTEM,

+            source: null,

+            user: null,

+            value: '{"default": "custom"}'

+          }

+        ]};

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

+      });

+

+      basketService.getFileExtension().subscribe(

+        ext => expect(ext).toBe('custom'),

+        err => expect(err).toBeUndefined());

+    })));

+

+    it('should return xml (default) if no preferences were found',

+        async(inject([BasketService, MockBackend], (basketService, mockBackend) => {

+

+      mockBackend.connections.subscribe(conn => {

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

+      });

+

+      basketService.getFileExtension().subscribe(

+        ext => expect(ext).toBe('xml'),

+        err => expect(err).toBeUndefined());

+    })));

+

+    it('should return xml (default) if no preferences value is invalid',

+        async(inject([BasketService, MockBackend], (basketService, mockBackend) => {

+

+      mockBackend.connections.subscribe(conn => {

+        let mockResponse = {

+          preferences: [

+          {

+            id: 2,

+            key: 'shoppingbasket.fileextensions',

+            scope: Scope.SYSTEM,

+            source: null,

+            user: null,

+            value: 'asdf'

+          }

+        ]};

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

+      });

+

+      basketService.getFileExtension().subscribe(

+        ext => expect(ext).toBe('xml'));

+    })));

+  });

+});

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..faf2c9e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/basket.service.ts
@@ -0,0 +1,133 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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';
+
+import { Http, Response, Headers, RequestOptions } from '@angular/http';
+import { HttpErrorHandler } from '../core/http-error-handler';
+import { PropertyService } from '../core/property.service';
+import {Observable} from 'rxjs/Observable';
+
+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.';
+  readonly preferenceFileextension = 'shoppingbasket.fileextension';
+  items: MDMItem[] = [];
+
+  constructor(private _pref: PreferenceService,
+              private http: Http,
+              private httpErrorHandler: HttpErrorHandler,
+              private _prop: PropertyService) {
+  }
+
+  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;
+  }
+
+  getBasketAsXml(basket: Basket) {
+    return this.http.post(this._prop.getUrl('/mdm/shoppingbasket'), basket)
+      .map(r => r.text());
+  }
+
+  getFileExtension() {
+    return this._pref.getPreferenceForScope(Scope.SYSTEM, this.preferenceFileextension)
+      .flatMap(prefs => prefs)
+      .map(pref => JSON.parse(pref.value).default + '')
+      .catch(e => {
+        console.log("Unable to parse value of preference '" + this.preferenceFileextension + "'!");
+        return Observable.of("xml");
+      })
+      .defaultIfEmpty("xml");
+  }
+
+  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..012c2d4
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.css
@@ -0,0 +1,50 @@
+@charset "ISO-8859-1";

+

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

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+.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..66b9285
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.html
@@ -0,0 +1,134 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+<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..7bb7f10
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.ts
@@ -0,0 +1,304 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+import { Component, Input, Output, EventEmitter, OnInit, ViewChild } from '@angular/core';
+import { DomSanitizer } from '@angular/platform-browser';
+import { ModalDirective } from 'ngx-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';
+
+import {MDMNotificationService} from '../core/mdm-notification.service';
+import {Observable} from 'rxjs/Observable';
+
+@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)
+  overwriteDialogComponent: OverwriteDialogComponent;
+
+  constructor(private _basketService: BasketService,
+              private queryService: QueryService,
+              private navigatorService: NavigatorService,
+              private sanitizer: DomSanitizer,
+              private NodeService: NodeService,
+              private notificationService: MDMNotificationService) {
+  }
+
+  removeSelected() {
+    this.tableViewComponent.selectedRows.map(r => r.getItem()).forEach(i => this._basketService.remove(i));
+  }
+
+  ngOnInit() {
+    this.NodeService.getRootNodes().subscribe(
+      envs => this.environments = envs,
+      error => this.notificationService.notifyError('Quellen kann nicht geladen werden.', error)
+    );
+    this.setItems(this._basketService.items);
+    this._basketService.itemsAdded$.subscribe(
+      items => this.addItems(items),
+      error => this.notificationService.notifyError('Auswahl kann nicht hinzugefügt werden.', error)
+    );
+    this._basketService.itemsRemoved$.subscribe(
+      items => this.removeItems(items),
+      error => this.notificationService.notifyError('Auswahl kann nicht entfernt werden.', error)
+    );
+    this.viewComponent.viewChanged$.subscribe(
+      () => this.setItems(this._basketService.items),
+      error => this.notificationService.notifyError('Ansicht kann nicht ausgewählt werden.', error)
+    );
+  }
+
+  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),
+          error => this.notificationService.notifyError('Items können nicht zum Warenkorb hinzugefügt werden', error)
+        )
+      );
+    }
+  }
+
+  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(
+        needSave => this.saveBasket2(needSave),
+        error => {
+          this.saveBasket2(false);
+          this.notificationService.notifyError('Ein Fehler ist aufgetreten. Der Warenkorb wurde nicht gespeichert.', error);
+        });
+    } 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,
+      error => this.notificationService.notifyError('Warenkorb kann nicht geladen werden.', error));
+  }
+
+  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());
+
+    this._basketService.getBasketAsXml(downloadContent).combineLatest(
+      this._basketService.getFileExtension(),
+      (xml, fileExtension) => this.saveXml(xml, fileExtension)
+    ).subscribe();
+  }
+
+  saveXml(xml: string, fileExtension: string) {
+    let blob = new Blob([xml], {
+         type: 'application/xml'
+    });
+
+    if (this.basketName && this.basketName.trim().length !== 0) {
+      FileSaver.saveAs(blob, this.basketName + '.' + fileExtension);
+    } else {
+      FileSaver.saveAs(blob, 'warenkorb.' + fileExtension);
+    }
+  }
+
+  saveJson(basket: Basket) {
+    let blob = new Blob([serialize(basket)], {
+         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..0fbd5db
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.module.ts
@@ -0,0 +1,28 @@
+ //********************************************************************************

+ //* Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ //*

+ //* See the NOTICE file(s) distributed with this work for additional

+ //* information regarding copyright ownership.

+ //*

+ //* This program and the accompanying materials are made available under the

+ //* terms of the Eclipse Public License v. 2.0 which is available at

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

+ //*

+ //* SPDX-License-Identifier: EPL-2.0

+ //*

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

+

+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/http-error-handler.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/http-error-handler.ts
new file mode 100644
index 0000000..76dcac2
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/http-error-handler.ts
@@ -0,0 +1,46 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

+

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

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

+

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

+

+@Injectable()

+export class HttpErrorHandler {

+

+  handleError(error: Response | any) {

+      let errMsg: string;

+      if (error instanceof Response) {

+        try {

+          const body = error.json() || '';

+          const err = body.error || JSON.stringify(body);

+          errMsg = `${error.status} - ${error.statusText || ''} ${err}`;

+        }

+        catch (e) {

+          if (isDevMode()) {

+            errMsg = `${error.status} - ${error.statusText || ''} ${error.text()}`;

+          } else {

+            errMsg = `Please contact the administrator. Status code: ${error.status} - ${error.statusText || ''}`;

+          }

+        }

+      } else {

+        errMsg = error.message ? error.message : error.toString();

+      }

+      return Observable.throw(errMsg);

+    }

+}

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..cc16334
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-core.module.ts
@@ -0,0 +1,81 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

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

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

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

+

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

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

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

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

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

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

+

+import { TreeModule, DataTableModule, SharedModule, ContextMenuModule, GrowlModule, MultiSelectModule } 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';

+

+import { TranslationPipe } from '../localization/translation.pipe';

+

+@NgModule({

+  imports: [

+    HttpModule,

+    FormsModule,

+    CommonModule,

+    Ng2BootstrapModule,

+    TabsModule.forRoot(),

+    TypeaheadModule.forRoot(),

+    DatepickerModule.forRoot(),

+    TreeModule,

+    DataTableModule,

+    SharedModule,

+    ContextMenuModule,

+    MultiSelectModule,

+    GrowlModule

+  ],

+  declarations: [

+    MDMNotificationComponent,

+    OverwriteDialogComponent,

+    TranslationPipe

+  ],

+  exports: [

+    CommonModule,

+    FormsModule,

+    Ng2BootstrapModule,

+    MultiSelectModule,

+    TreeModule,

+    DataTableModule,

+    SharedModule,

+    ContextMenuModule,

+    GrowlModule,

+    MDMNotificationComponent,

+    OverwriteDialogComponent,

+    TranslationPipe

+  ],

+  providers: [

+      PositioningService,

+      ComponentLoaderFactory,

+

+      PropertyService,

+      PreferenceService

+    ],

+})

+export class MDMCoreModule { }

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

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

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

+

+@Injectable()

+export class MDMErrorHandler extends ErrorHandler {

+

+  constructor(private notificationService: MDMNotificationService) {

+    super(true);

+  }

+

+  handleError(error) {

+    this.notificationService.notifyError("Applikationsfehler",

+      "Es ist ein Applikationsfehler aufgetreten. Für eine detailierte "

+      + "Fehlermeldung öffnen Sie bitte die Entwicklerkonsole Ihres Browsers.");

+

+    super.handleError(error);

+  }

+}

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..1211186
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-item.ts
@@ -0,0 +1,36 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

+

+export class MDMItem {

+  source: string;

+  type: string;

+  id: string;

+

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

+    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..ca9f848
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-notification.component.ts
@@ -0,0 +1,43 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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.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..3aa4349
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/mdm-notification.service.ts
@@ -0,0 +1,44 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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: any) {

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

+  }

+

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

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

+  }

+

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

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

+  }

+

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

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

+  }

+

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

+    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..9c29a87
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/overwrite-dialog.component.html
@@ -0,0 +1,35 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..682247f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/overwrite-dialog.component.ts
@@ -0,0 +1,46 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

+import { ModalDirective } from 'ngx-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')

+  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..956d24f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/preference.service.spec.ts
@@ -0,0 +1,103 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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';

+import {HttpErrorHandler} from '../core/http-error-handler';

+

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

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        PropertyService,

+        PreferenceService,

+        MockBackend,

+        BaseRequestOptions,

+        HttpErrorHandler,

+        {

+          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..e3d0eb3
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/preference.service.ts
@@ -0,0 +1,116 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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';

+import { HttpErrorHandler } from './http-error-handler';

+

+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 httpErrorHandler: HttpErrorHandler,

+              private _prop: PropertyService) {

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

+  }

+

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

+      if (key == null) {

+          key = '';

+      }

+

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

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

+          .catch(this.handleError);

+  }

+

+  getPreference(key?: string) {

+      if (key == null) {

+          key = '';

+      }

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

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

+          .catch(this.handleError);

+  }

+

+  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)

+      .catch(this.handleError);

+  }

+

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

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

+  }

+

+  private handleError(e: Error | any) {

+    if (e instanceof Response) {

+      let response = <Response> e;

+      if (response.status != 200) {

+        return Observable.throw("Could not request preferences! Please check if application server is running and preference database is configured correctly.");

+      }

+    }

+    return this.httpErrorHandler.handleError(e);

+  }

+}

diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/core/property.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/core/property.service.ts
new file mode 100644
index 0000000..c537d8b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/core/property.service.ts
@@ -0,0 +1,31 @@
+// ********************************************************************************
+// * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+// *
+// * See the NOTICE file(s) distributed with this work for additional
+// * information regarding copyright ownership.
+// *
+// * This program and the accompanying materials are made available under the
+// * terms of the Eclipse Public License v. 2.0 which is available at
+// * http://www.eclipse.org/legal/epl-2.0.
+// *
+// * SPDX-License-Identifier: EPL-2.0
+// *
+// ********************************************************************************
+ 
+import {Injectable} from '@angular/core';
+
+@Injectable()
+export class PropertyService {
+  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.spec.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.service.spec.ts
new file mode 100644
index 0000000..e37cf4a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.service.spec.ts
@@ -0,0 +1,184 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

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

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

+

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

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

+import {HttpErrorHandler} from '../core/http-error-handler';

+

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

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        MockBackend,

+        BaseRequestOptions,

+        HttpErrorHandler,

+        {

+          provide: Http,

+          useFactory: (mockBackend, options) => {

+            return new Http(mockBackend, options);

+          },

+          deps: [MockBackend, BaseRequestOptions]

+        },

+        PropertyService,

+        ContextService

+      ]

+    });

+  });

+

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

+    it('should merge value of one attribute', async(inject([ContextService], (contextService) => {

+      let attribute1 : ContextAttribute = {

+        "name" : "size",

+        "value" : "95R16",

+        "unit" : "",

+        "dataType" : "STRING"

+      }

+      let attributes = [ attribute1 ];

+      let contextIndex = 2;

+      let resultAttributes: MergedContextAttribute[] = [];

+

+      expect(contextService.mergeAttributes(attributes, contextIndex, resultAttributes)).toEqual(

+        [{

+          "name" : "size",

+          "value" : [undefined, undefined, "95R16"],

+          "unit" : "",

+          "dataType" : "STRING"

+        }]

+      );

+    })))

+

+    it('should merge values of multiple attributes', async(inject([ContextService], (contextService) => {

+      let attribute1 : ContextAttribute = {

+        "name" : "size",

+        "value" : "95R16",

+        "unit" : "",

+        "dataType" : "STRING"

+      }

+      let attribute2 : ContextAttribute = {

+        "name" : "side",

+        "value" : "Left",

+        "unit" : "",

+        "dataType" : "STRING"

+      }

+

+      let attributes = [ attribute1, attribute2 ];

+      let contextIndex = 0;

+      let resultAttributes: MergedContextAttribute[] = [];

+

+      expect(contextService.mergeAttributes(attributes, contextIndex, resultAttributes)).toEqual(

+        [{

+          "name" : "size",

+          "value" : ["95R16"],

+          "unit" : "",

+          "dataType" : "STRING"

+        }, {

+          "name" : "side",

+          "value" : ["Left"],

+          "unit" : "",

+          "dataType" : "STRING"

+        }]

+      );

+    })))

+  });

+

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

+    it('should merge attributes values of all context components', async(inject([ContextService], (contextService) => {

+      let data = {

+        "contextOrdered" : {

+          "UNITUNDERTEST" : [{

+              "name" : "FL_tyre",

+              "id" : "38",

+              "type" : "ContextComponent",

+              "sourceType" : "tyre",

+              "sourceName" : "MDM",

+              "attributes" : [{

+                  "name" : "size",

+                  "value" : "95R16",

+                  "unit" : "",

+                  "dataType" : "STRING"

+                }

+              ]

+            }

+          ]

+        },

+        "contextMeasured" : {

+          "UNITUNDERTEST" : [{

+              "name" : "FL_tyre",

+              "id" : "39",

+              "type" : "ContextComponent",

+              "sourceType" : "tyre",

+              "sourceName" : "MDM",

+              "attributes" : [{

+                  "name" : "size",

+                  "value" : "95R17",

+                  "unit" : "",

+                  "dataType" : "STRING"

+                }

+              ]

+            }, {

+              "name" : "engine",

+              "id" : "12",

+              "type" : "ContextComponent",

+              "sourceType" : "engine",

+              "sourceName" : "MDM",

+              "attributes" : [{

+                  "name" : "cylinders",

+                  "value" : "2",

+                  "unit" : "",

+                  "dataType" : "STRING"

+                }

+              ]

+            }

+          ]

+        }

+      };

+

+      let mergedData = contextService.mergeContextRoots([data.contextOrdered, data.contextMeasured]);

+

+      expect(mergedData).toEqual({

+        "UNITUNDERTEST": [{

+          "name" : "FL_tyre",

+          "id" : "38",

+          "type" : "ContextComponent",

+          "sourceType" : "tyre",

+          "sourceName" : "MDM",

+          "attributes" : [{

+            "name" : "size",

+            "value" : ["95R16", "95R17"],

+            "unit" : "",

+            "dataType" : "STRING"

+          }]

+        }, {

+          "name" : "engine",

+          "id" : "12",

+          "type" : "ContextComponent",

+          "sourceType" : "engine",

+          "sourceName" : "MDM",

+          "attributes" : [{

+            "name" : "cylinders",

+            "value" : [undefined, "2"],

+            "unit" : "",

+            "dataType" : "STRING"

+          }]

+        }]

+      });

+    })));

+  });

+});

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..1c23a5b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.service.ts
@@ -0,0 +1,200 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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 {HttpErrorHandler} from '../core/http-error-handler';
+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 httpErrorHandler: HttpErrorHandler,
+              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.http.get(url)
+      .map((res) => {
+        let data = res.json().data;
+        let context = this.mergeContextRoots([data[0].contextOrdered, data[0].contextMeasured]);
+        return <{}> context;
+      })
+      .catch(this.httpErrorHandler.handleError);
+  }
+
+  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.httpErrorHandler.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;
+  }
+
+  /** Merges several ContextRoots by merging their ContextComponents */
+  private mergeContextRoots(contexts) {
+    let result : { [context: string]: MergedContextComponent[] } = {};
+
+    for (let i = 0; i < contexts.length; ++i) {
+      for (let contextType in contexts[i]) {
+        if (contexts[i].hasOwnProperty(contextType)) {
+          result[contextType] = this.mergeComponents(contexts[i][contextType], i, result[contextType]);
+        }
+      }
+    }
+    return result;
+  }
+
+  /** Merges several ContextComponents by merging their ContextAttributes */
+  private mergeComponents(contextComponents: any, contextIndex: number, resultComponents: MergedContextComponent[]) {
+    if (!Array.isArray(contextComponents)) {
+      return resultComponents;
+    }
+
+    let result = resultComponents || [];
+
+    for (let contextComponent of contextComponents) {
+      if (!(contextComponent instanceof Object)) { continue; }
+
+      let resultComponent = result.find(cc => cc.name === contextComponent['name']);
+
+      if (!resultComponent) {
+        resultComponent = JSON.parse(JSON.stringify(contextComponent))
+        resultComponent['attributes'] = [];
+        result.push(resultComponent);
+      }
+
+      resultComponent['attributes'] = this.mergeAttributes(contextComponent['attributes'], contextIndex, resultComponent['attributes']);
+    }
+    return result;
+  }
+
+  /** Merges a array of ContextAttributes to a MergedContextAttribute
+   * and adds it to the given resultAttributes array. Merging the ContextAttributes
+   * copies the values of all properties (which should be identical), except the
+   * value property. The value property in MergedContextAttribute is an array
+   * with all values of the value property from all ContextAttributes.
+   */
+  private mergeAttributes(attributes: ContextAttribute[], contextIndex: number, resultAttributes: MergedContextAttribute[]) {
+    if (!Array.isArray(attributes)) {
+      return resultAttributes;
+    };
+
+    let result = resultAttributes || [];
+
+    for (let attribute of attributes) {
+      if (!(attribute instanceof Object)) { continue; }
+
+      let resultAttribute = result.find(a => a.name === attribute['name']);
+
+      if (!resultAttribute) {
+        resultAttribute = JSON.parse(JSON.stringify(attribute))
+        resultAttribute['value'] = [];
+        result.push(resultAttribute);
+      }
+      resultAttribute['value'][contextIndex] = attribute['value'];
+    }
+
+    return resultAttributes;
+  }
+}
+
+/** Represents a ContextAttribute */
+export class ContextAttribute {
+  name: string;
+  value: string;
+  unit: string;
+  dataType: string;
+}
+
+/** Represents multiple ContextAttributes with their value properties merged to an array */
+export class MergedContextAttribute {
+  name: string;
+  value: string[];
+  unit: string;
+  dataType: string;
+}
+
+/** Represents merged ContextComponents */
+export class MergedContextComponent {
+  name: string;
+  id: string;
+  type: string;
+  sourcType: string;
+  sourceName: string;
+  attributes: MergedContextAttribute[];
+}
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..f7e6dfe
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/context.ts
@@ -0,0 +1,32 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..652249a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/detail-view.service.spec.ts
@@ -0,0 +1,85 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..91b2119
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/detail-view.service.ts
@@ -0,0 +1,77 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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),

+            error => this.notificationService.notifyWarn('Einstellung für zu ignorirende Attribute kann nicht geladen werden.', error));

+    }

+

+    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..f5e0a08
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-descriptive-data.component.html
@@ -0,0 +1,92 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+<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..574c20b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-descriptive-data.component.ts
@@ -0,0 +1,132 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+import {Component, OnInit, Input, OnChanges, SimpleChange} from '@angular/core';
+import { Router, ActivatedRoute, Params } from '@angular/router';
+
+import { AccordionComponent, AccordionModule } from 'ngx-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';
+
+import {MDMNotificationService} from '../core/mdm-notification.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[];
+  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,
+              private notificationService: MDMNotificationService) {}
+
+  ngOnInit() {
+    this.status = this.StatusLoading;
+
+    this.loadContext(this.navigatorService.getSelectedNode());
+
+    this.route.params
+        .subscribe(
+          params => this.setContext(params['context']),
+          error => this.notificationService.notifyError('Bereich kann nicht geladen werden.', error)
+    );
+
+    this.navigatorService.selectedNodeChanged
+        .subscribe(
+          node => this.loadContext(node),
+          error => this.notificationService.notifyError('Daten können nicht geladen werden.', error));
+  }
+
+  setContext(context: string) {
+    this.context = context;
+  }
+
+  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.notificationService.notifyError('Kontext kann nicht geladen werden.', 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';
+  }
+}
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..df6e127
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-routing.module.ts
@@ -0,0 +1,41 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..43fc03f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-view.component.html
@@ -0,0 +1,40 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<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()">
+    {{selectedNode.type | translate}}
+  </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>{{selectedNode.type | translate: 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..2056ff2
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail-view.component.ts
@@ -0,0 +1,90 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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 {NavigatorService} from '../navigator/navigator.service';
+import { DetailViewService } from './detail-view.service';
+
+import {MDMNotificationService} from '../core/mdm-notification.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,
+              private notificationService: MDMNotificationService) {}
+
+  ngOnInit() {
+    this.onSelectedNodeChange(this.navigatorService.getSelectedNode());
+    this.subscription = this.navigatorService.selectedNodeChanged.subscribe(
+          node => this.onSelectedNodeChange(node),
+          error => this.notificationService.notifyError('Knoten kann nicht aktualisiert werden.', error)
+        );
+  }
+
+  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;
+  }
+}
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..5855f73
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.component.html
@@ -0,0 +1,30 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<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..e0a9502
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.component.ts
@@ -0,0 +1,37 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..c07c69c
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/mdm-detail.module.ts
@@ -0,0 +1,51 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..751757a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/sensor.component.html
@@ -0,0 +1,42 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..a92e501
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/details/sensor.component.ts
@@ -0,0 +1,109 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

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

+

+import { AccordionComponent, AccordionModule } from 'ngx-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';

+

+import {MDMNotificationService} from '../core/mdm-notification.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,

+              private notificationService: MDMNotificationService) {}

+

+  ngOnInit() {

+    this.status = this.StatusLoading;

+    this.route.params

+        .subscribe(

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

+          error => this.notificationService.notifyError('Bereich kann nicht geladen werden.', error)

+    );

+

+    this.navigatorService.selectedNodeChanged

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

+        error => this.notificationService.notifyError('Daten können nicht geladen werden.', error)

+    );

+  }

+

+  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.notificationService.notifyError('Beschreibende Daten können nicht geladen werden.', 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';

+    }

+  }

+}

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..6a6afca
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example-routing.module.ts
@@ -0,0 +1,32 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..fac45f5
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example.component.ts
@@ -0,0 +1,23 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..0c23969
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/mdm-example.module.ts
@@ -0,0 +1,31 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..adcbb2a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/example-module/readme.md
@@ -0,0 +1,27 @@
+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

+

+## Copyright and License ##

+Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+

+ See the NOTICE file(s) distributed with this work for additional

+ information regarding copyright ownership.

+

+ This program and the accompanying materials are made available under the

+ terms of the Eclipse Public License v. 2.0 which is available at

+ http://www.eclipse.org/legal/epl-2.0.

+

+ SPDX-License-Identifier: EPL-2.0

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..5bee396
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/filerelease.service.ts
@@ -0,0 +1,146 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+import {Injectable} from '@angular/core';
+import {Http, Response, Headers, RequestOptions} from '@angular/http';
+import {Observable} from 'rxjs/Observable';
+import {HttpErrorHandler} from '../core/http-error-handler';
+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 httpErrorHandler: HttpErrorHandler,
+              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.httpErrorHandler.handleError);
+  }
+
+  delete(release: Release) {
+    return this.http.delete(this.url + '/' + release.identifier)
+      .catch(this.httpErrorHandler.handleError);
+  }
+
+  approve(release: Release) {
+    release.state = 'RELEASE_APPROVED';
+    return this.update(release)
+      .catch(this.httpErrorHandler.handleError);
+  }
+
+  reject(release: Release) {
+    release.state = 'RELEASE_REJECTED';
+    return this.update(release)
+      .catch(this.httpErrorHandler.handleError);
+  }
+
+  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.httpErrorHandler.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.httpErrorHandler.handleError);
+  }
+
+  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: string;
+  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..859fd21
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-create.component.html
@@ -0,0 +1,66 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<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..6ffcd1f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-create.component.ts
@@ -0,0 +1,71 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+import {Component, Input, Output, EventEmitter, ViewChild} from '@angular/core';
+import { ModalDirective } from 'ngx-bootstrap';
+import {Release, FilereleaseService} from './filerelease.service';
+import {Node} from '../navigator/node';
+
+import {MDMNotificationService} from '../core/mdm-notification.service';
+
+@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;
+  options = ['PAK2RAW', 'PAK2ATFX'];
+  expire = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+
+  @ViewChild('lgModal')
+  lgModal: ModalDirective;
+
+  constructor(private service: FilereleaseService,
+              private notificationService: MDMNotificationService) {}
+
+  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.notificationService.notifyError('Release kann nicht erzeugt werden.', 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..fe9b68b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-display.component.html
@@ -0,0 +1,49 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<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..013f78e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease-display.component.ts
@@ -0,0 +1,42 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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/src/app/filerelease/mdm-filerelease.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.component.html
new file mode 100644
index 0000000..3ace6f8
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.component.html
@@ -0,0 +1,128 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<style>
+  .container-fluid {
+    padding: 0;
+  }
+</style>
+<div class="container-fluid">
+  <div class="box" style="margin-bottom: 15px;">
+    <div class="well">
+      <h2>Freigabeantragseingang</h2>
+    </div>
+    <table class="table table-hover">
+      <thead>
+        <tr>
+          <th>Versuchsname</th>
+          <th>Antragsteller</th>
+          <th>Freigabedauer</th>
+          <th>freigegeben bis</th>
+          <th>Status</th>
+          <th>Format</th>
+          <th></th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr *ngFor="let data of incoming" [ngClass]="getState(data)" (click)=setData(data) (click)='setEvent("approve")' (click)=lgModal.show()>
+          <td>{{data.name}}</td>
+          <td>{{data.sender}}</td>
+          <td>{{data.validity}} Tage</td>
+          <td>{{getDate(data.expire)}}</td>
+          <td>{{getTransState(data.state)}}</td>
+          <td>{{getFormat(data.format)}}</td>
+          <td></td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</div>
+<div class="container-fluid">
+  <div class="box">
+    <div class="well">
+      <h2>Freigabeantragsausgang</h2>
+    </div>
+    <table class="table table-hover">
+      <thead>
+        <tr>
+          <th>Versuchsname</th>
+          <th>Messungsverantwortlicher</th>
+          <th>Freigabedauer</th>
+          <th>freigegeben bis</th>
+          <th>Status</th>
+          <th>Format</th>
+          <th>Download</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr *ngFor="let data of outgoing" [ngClass]="getState(data)" (click)=setData(data) (click)='setEvent("edit")'>
+          <td (click)=lgModal.show()>{{data.name}}</td>
+          <td (click)=lgModal.show()>{{data.receiver}}</td>
+          <td (click)=lgModal.show()>{{data.validity}} Tage</td>
+          <td (click)=lgModal.show()>{{getDate(data.expire)}}</td>
+          <td (click)=lgModal.show()>{{getTransState(data.state)}}</td>
+          <td (click)=lgModal.show()>{{getFormat(data.format)}}</td>
+          <td>
+            <a *ngIf="data.state == 'RELEASE_RELEASED'" href="{{dataHost}}{{data.fileLink}}" download><span class="glyphicon glyphicon-download"></span></a>
+            <span *ngIf="data.state != 'RELEASE_RELEASED'" class="glyphicon glyphicon-ban-circle"></span>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</div>
+
+<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-display [release]=release ></mdm-filerelease-display>
+        <div class="btn-group" *ngIf='event=="approve"'>
+          <button type="button" class="btn btn-success" (click)="approveRelease()" (click)="lgModal.hide()" [disabled]="isReleaseable()">Genehmigen</button>
+          <button type="button" class="btn btn-danger" (click)="smModal.show()" (click)="lgModal.hide()" [disabled]="isReleaseable()">Ablehnen</button>
+        </div>
+        <div class="btn-group" *ngIf='event=="edit"'>
+          <button type="button" class="btn btn-default" (click)="deleteRelease()" (click)="lgModal.hide()" [disabled]=isDeletable()>Anfrage zurückziehen/entfernen</button>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<div bsModal #smModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
+  <div class="modal-dialog modal-sm">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" aria-label="Close" (click)="smModal.hide()">
+          <span aria-hidden="true">&times;</span>
+        </button>
+        <h4 class="modal-title">Begründung</h4>
+      </div>
+      <div class="modal-body">
+        <textarea #reason cols="35" rows="4" class="form-control"></textarea>
+        <div class="btn-group">
+          <button type="button" class="btn btn-danger" (click)="rejectRelease(reason.value)" (click)=smModal.hide() (click)="reason.value = ''">Ablehnen</button>
+          <button type="button" class="btn btn-default" (click)="smModal.hide()" (click)="reason.value = ''">Abbrechen</button>
+        </div>
+      </div>
+    </div>
+  </div>
+</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..adbe0f7
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.component.ts
@@ -0,0 +1,121 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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 'ngx-bootstrap';
+
+import {MDMNotificationService} from '../core/mdm-notification.service';
+
+@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[] = [];
+  release: Release = new Release;
+  event = 'display';
+  dataHost: string;
+
+  @ViewChild('lgModal')
+  lgModal: ModalDirective;
+
+  @ViewChild('smModal')
+  smModal: ModalDirective;
+
+  constructor(private service: FilereleaseService,
+              private prop: PropertyService,
+              private notificationService: MDMNotificationService) {
+    this.dataHost = prop.getDataHost();
+  }
+
+  ngOnInit() {
+    this.getReleases();
+  }
+
+  getReleases() {
+    this.service.readOutgoging().subscribe(
+      releases => this.outgoing = releases,
+      error => this.notificationService.notifyError('Ausgehendes Release kann nicht gelesen werden.', error)
+    );
+    this.service.readIncomming().subscribe(
+      releases => this.incoming = releases,
+      error => this.notificationService.notifyError('Eingehendes Release kann nicht gelesen werden.', 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.notificationService.notifyError('Fehler beim Zurückweisen des Release', error)
+    );
+  }
+  approveRelease() {
+    this.service.approve(this.release).subscribe(
+      release => this.updateList(release),
+      error => this.notificationService.notifyError('Fehler beim Bestätigen des Release', 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..29f08a1
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/filerelease/mdm-filerelease.module.ts
@@ -0,0 +1,40 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..f776c2b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/localization/localization.service.ts
@@ -0,0 +1,73 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+import {Injectable} 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';
+import {HttpErrorHandler} from '../core/http-error-handler';
+import {MDMNotificationService} from '../core/mdm-notification.service';
+
+@Injectable()
+export class LocalizationService {
+
+  private _nodeUrl: string;
+
+  private cache: Observable<Localization[]>;
+
+  constructor(private http: Http,
+              private httpErrorHandler: HttpErrorHandler,
+              private _prop: PropertyService,
+              private _node: NodeService,
+              private notificationService: MDMNotificationService) {
+
+    this._nodeUrl = _prop.getUrl('/mdm/environments');
+  }
+
+  // Cashes valueLists if cash is empty. Then returns observable containing cached valueLists.
+  getLocalizations() {
+    if (!this.cache) {
+      this.cache = this._node.getNodes(undefined)
+                .flatMap(envs => this.initLocalizations(envs))
+                .publishReplay(1)
+                .refCount();
+    }
+    return this.cache;
+  }
+
+  private initLocalizations(envs: Node[]) {
+    return Observable.forkJoin(envs.map(env => this.getLocalization(env)))
+                     .map(locs => locs.reduce((a, b) => a.concat(b), []));
+  }
+
+  private getLocalization(node: Node): Observable<Localization[]> {
+    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.httpErrorHandler.handleError);
+  }
+}
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..d097709
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/localization/localization.ts
@@ -0,0 +1,19 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+export class Localization {
+  name: string;
+  localizedName: string;
+}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/localization/translation.pipe.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/localization/translation.pipe.ts
new file mode 100644
index 0000000..18b585a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/localization/translation.pipe.ts
@@ -0,0 +1,47 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+import { Pipe, PipeTransform } from '@angular/core';

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

+import {Localization} from './localization';

+

+@Pipe({

+  name: 'translate',

+  pure: false

+})

+export class TranslationPipe implements PipeTransform {

+

+  private translation: string;

+

+  constructor(private localService: LocalizationService) {}

+

+  transform(type: string, attr?: string): any {

+    return type ? this.getTrans(type, attr) : type;

+  }

+

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

+    this.localService.getLocalizations()

+                     .map(locs => this.getTranslation(locs, type, attr))

+                     .subscribe(t => this.translation = t);

+    return this.translation;

+  }

+

+  private getTranslation(locs: Localization[], type: string, attr: string) {

+    let trans = attr ? type + '.' + attr : type;

+    let temp = locs.find(l => l.name === trans);

+    return temp ? temp.localizedName : trans;

+  }

+

+}

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..a220e62
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules-routing.module.ts
@@ -0,0 +1,38 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..3ea7694
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.component.html
@@ -0,0 +1,23 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..d927476
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.component.ts
@@ -0,0 +1,31 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..a616714
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/modules/mdm-modules.module.ts
@@ -0,0 +1,39 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..a361c7b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view-routing.module.ts
@@ -0,0 +1,45 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..6935a16
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.css
@@ -0,0 +1,63 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+.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..d902adb
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.html
@@ -0,0 +1,53 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+<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..3d44c8e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.component.ts
@@ -0,0 +1,121 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+import {Component, ViewEncapsulation, OnInit, OnDestroy} from '@angular/core';
+
+import { BsDropdownModule, AccordionConfig, BsDropdownConfig } from 'ngx-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';
+
+import {MDMNotificationService} from '../core/mdm-notification.service';
+
+@Component({
+  selector: 'mdm-navigator-view',
+  templateUrl: 'mdm-navigator-view.component.html',
+  styleUrls: [ './mdm-navigator-view.component.css' ],
+  providers: [BsDropdownConfig, 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,
+              private notificationService: MDMNotificationService) {}
+
+  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,
+          error => this.notificationService.notifyError('Nodeprovider kann nicht aktualisiert werden.', error)
+        );
+  }
+
+  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..a254f77
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator-view/mdm-navigator-view.module.ts
@@ -0,0 +1,46 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..8e38405
--- /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..ccd0e23
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.MockNodes.ts
@@ -0,0 +1,101 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+export const MockEnvNodes = {
+  'type': 'Environment',
+  'data': [
+    {
+      'name': 'Test Environment',
+      'id': 'id1',
+      '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': 'id10',
+        'type': 'Test',
+        'sourceType': 'Test',
+        'sourceName': 'Test Environment',
+        'attributes': [
+          {
+            'name': 'DateClosed',
+            'value': '',
+            'unit': '',
+            'dataType': 'DATE'
+          }
+        ]
+      },
+      {
+        'name': 'Test 2',
+        'id': 'id20',
+        'type': 'Test',
+        'sourceType': 'Test',
+        'sourceName': 'Test Environment',
+        'attributes': [
+          {
+            'name': 'DateClosed',
+            'value': '',
+            'unit': '',
+            'dataType': 'DATE'
+          }
+        ]
+      },
+      {
+        'name': 'Test 3',
+        'id': 'id30',
+        '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..8247f89
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.html
@@ -0,0 +1,29 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<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..deaafa6
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.spec.ts
@@ -0,0 +1,79 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..2fc3d74
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.component.ts
@@ -0,0 +1,236 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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';
+
+import {MDMNotificationService} from '../core/mdm-notification.service';
+
+@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,
+    private notificationService: MDMNotificationService) {
+  }
+
+  ngOnInit() {
+    this.reloadTree();
+    this.nodeproviderService.nodeProviderChanged.subscribe(
+        np => this.reloadTree(),
+        error => this.notificationService.notifyError('Navigationsbaum kann nicht aktualisiert werden.', error)
+      );
+
+    this.navigatorService.onOpenInTree
+      .subscribe(
+        items => this.openInTree(items),
+        error => this.notificationService.notifyError('Knoten kann nicht im Navigationsbaum geöffnet werden.', error)
+      );
+  }
+
+  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,
+          error => this.notificationService.notifyError('Knoten können nicht geladen werden.', error)
+        );
+    }
+  }
+
+  reloadTree() {
+    this.nodeService.getRootNodes().subscribe(
+      n => this.nodes = n.map(node => this.mapNode(node)),
+      error => this.notificationService.notifyError('Navigationsbaum kann nicht aktualisiert werden.', error)
+    );
+  }
+
+  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)));
+  }
+
+  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),
+          error => this.notificationService.notifyError('Knoten kann nicht geöffnet werden.', error)
+        );
+    }
+  }
+
+  expander(item: MDMItem, current: TreeNode, pathTypes: string[], iii: number) {
+    let expandList: string[] = [];
+    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),
+                      error => this.notificationService.notifyError('Knoten kann nicht geöffnet werden.', error)
+                    );
+              };
+              this.scrollToSelectionPrimeNgDataTable(node);
+            }
+          });
+        },
+        error => this.notificationService.notifyError('Knoten kann nicht geöffnet werden.', error)
+      );
+  }
+
+  /**
+   * 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..f50f0ca
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/mdm-navigator.module.ts
@@ -0,0 +1,34 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..9856b3f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/navigator.service.ts
@@ -0,0 +1,64 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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';

+

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

+

+@Injectable()

+export class NavigatorService {

+

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

+

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

+  private selectedNode: Node;

+

+  constructor(private nodeService: NodeService,

+              private notificationService: MDMNotificationService) {

+

+  }

+

+  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),

+          error => this.notificationService.notifyError('Item konnte nicht gesetzt werden.', error)

+        );

+  }

+

+  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..6d24ef5
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.service.ts
@@ -0,0 +1,140 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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 {HttpErrorHandler} from '../core/http-error-handler';
+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 httpErrorHandler: HttpErrorHandler,
+              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.httpErrorHandler.handleError);
+  }
+
+  searchFT(query, env) {
+    return this.http.get(this._nodeUrl + '/' + env + '/search?q=' + query)
+              .map(res => plainToClass(Node, res.json().data))
+              .catch(this.httpErrorHandler.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.httpErrorHandler.handleError);
+  }
+
+  deleteNode(node: Node) {
+    return this.http.delete(this.getUrl(node))
+      .map(res => plainToClass(Node, res.json().data))
+      .catch(this.httpErrorHandler.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(this.httpErrorHandler.handleError);
+  }
+
+  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))
+      .catch(this.httpErrorHandler.handleError);
+  }
+
+  getNodesByUrl(url: string) {
+    return this.http.get(this._nodeUrl + url)
+      .map(res => plainToClass(Node, res.json().data))
+      .catch(this.httpErrorHandler.handleError);
+  }
+
+  private getUrl(node: Node) {
+    return this._nodeUrl + '/' + node.sourceName + '/' + node.type + '/' + node.id;
+  }
+}
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..a5e72c2
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.ts
@@ -0,0 +1,49 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+import {Type, Exclude, plainToClass, serialize, deserializeArray} from 'class-transformer';
+
+export class Node {
+  name: string;
+  id: string;
+  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..9dbbc74
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider.service.spec.ts
@@ -0,0 +1,82 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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';

+import {HttpErrorHandler} from '../core/http-error-handler';

+

+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,

+        HttpErrorHandler,

+        NodeService]

+    });

+  });

+

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

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

+      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', 'id1');

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

+

+      expect(query).toEqual(undefined);

+  }));

+

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

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

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

+

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

+  }));

+});

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..71568f1
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/nodeprovider.service.ts
@@ -0,0 +1,110 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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.')

+      .map(prefs => prefs.map(p => JSON.parse(p.value)))

+      .subscribe(

+        nodeproviders => this.nodeproviders = nodeproviders,

+        error => this.notificationService.notifyError('Nodeprovider kann nicht aus den Einstellungen geladen werden.', error)

+      );

+  }

+

+  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..5a52cad
--- /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/notice.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/notice.component.ts
new file mode 100644
index 0000000..2e6fffc
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/notice.component.ts
@@ -0,0 +1,25 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

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

+

+@Component({

+  selector: 'notice',

+  templateUrl: './NOTICE.html',

+  styles: ['pre { overflow-y: scroll; height: 400px; }']

+})

+export class NoticeComponent {

+

+}

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..eac7bf2
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/edit-searchFields.component.html
@@ -0,0 +1,101 @@
+<!-- ********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..6edd7dd
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/edit-searchFields.component.ts
@@ -0,0 +1,212 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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 {TypeaheadMatch} from 'ngx-bootstrap/typeahead';

+import {ModalDirective} from 'ngx-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),

+        error => this.notificationService.notifyError('Ausgewählter Knoten kann nicht aktualisiert werden.', error)

+      );

+  }

+

+  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..ebadc5f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/filter.service.spec.ts
@@ -0,0 +1,105 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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('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..620d9d8
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/filter.service.ts
@@ -0,0 +1,148 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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 readonly EMPTY_FILTER = new SearchFilter(this.NO_FILTER_NAME, [], 'Test', '', []);

+

+  constructor(private http: Http,

+              private _prop: PropertyService,

+              private preferenceService: PreferenceService) {

+  }

+

+  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..9df0223
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.component.html
@@ -0,0 +1,265 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<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);
+  }
+  :host /deep/ #envs .ui-multiselect {
+    padding: 0;
+    width: 200px;
+    height: 34px;
+  }
+  :host /deep/ #envs .ui-multiselect .ui-multiselect-label {
+    font-weight: normal;
+    padding: 6px 2em 4px 0.5em;
+    margin: 0;
+  }
+
+  :host /deep/ #envs .ui-multiselect:not(.ui-state-disabled):hover {
+    background-color: #e6e6e6;
+    border-color: #adadad;
+  }
+  :host /deep/ #envs .ui-multiselect:not(.ui-state-disabled):hover .ui-multiselect-trigger {
+    background-color: #e6e6e6;
+  }
+
+</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>
+    <p-multiSelect name="envs" id="envs" [options]="dropdownModel" [(ngModel)]="selectedEnvs" (onChange)="selectedEnvironmentsChanged()" title="{{TtlSelectSource}}" class="btn-group dropdown"></p-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 einer 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 Spezialsymbole gesucht werden soll, m&uuml;ssen diese 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 id="advancedSearch" [isOpen]="isAdvancedSearchOpen" #advancedSearch [ngClass]="{'greyed': !isAdvancedSearchActive}">
+    <div accordion-heading class="thinheader">
+      <div class="row">
+        <div class="col-xs-9">
+          <div class="pull-left">
+            <input type="checkbox" [(ngModel)]="isAdvancedSearchActive" (click)="onCheckBoxClick($event)" title="{{getAdvancedSearchCbxTitle()}}"/> &nbsp;{{LblAdvancedSearch}} &nbsp;&nbsp;
+            <button type="button" title="{{TtlNewSearchFields}}" class="btn btn-default" (click)="showSearchFieldsEditor($event)" [disabled]=!isAdvancedSearchActive><span class="glyphicon glyphicon-plus"></span></button>
+            <button type="button" title="{{TtlEditSearchFields}}" class="btn btn-default" (click)="showSearchFieldsEditor($event,currentFilter.conditions)" [disabled]=!isAdvancedSearchActive><span class="glyphicon glyphicon-edit"></span></button>
+            <button type="button" class="btn btn-default" (click)="showSaveModal($event)" title="{{TtlSaveSearchFilter}}" [disabled]=!isAdvancedSearchActive><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]=!isAdvancedSearchActive><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]="!isAdvancedSearchActive"
+                    [selectedEnvs]="selectedEnvironments"
+                    (onRemove)="removeCondition($event)">
+                </tr>
+              </table>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </accordion-group>
+  <accordion-group  id="searchResults" [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"
+            [loading]="loading">
+          </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..bb66555
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.component.ts
@@ -0,0 +1,462 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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 {TypeaheadMatch} from 'ngx-bootstrap/typeahead';
+
+import {ModalDirective} from 'ngx-bootstrap';
+import {AccordionPanelComponent} from 'ngx-bootstrap/accordion';
+
+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, plainToClass, serialize, deserialize} from 'class-transformer';
+import {SelectItem} from 'primeng/primeng';
+
+@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;
+  isAdvancedSearchActive = true;
+  isSearchResultsOpen = false;
+
+  layout: SearchLayout = new SearchLayout;
+
+  public dropdownModel: SelectItem[] = [];
+  public selectedEnvs: string[] = [];
+
+  searchFields: { group: string, attribute: string }[] = [];
+
+  subscription: any;
+  searchExecuted = false;
+
+  selectedRow: SearchFilter;
+  lazySelectedRow: SearchFilter;
+  loading = false;
+
+  contextMenuItems: MenuItem[] = [
+    { label: 'In Warenkorb legen', icon: 'glyphicon glyphicon-shopping-cart', command: (event) => this.addSelectionToBasket() }
+  ];
+
+  @ViewChild(ViewComponent)
+  viewComponent: ViewComponent;
+
+  @ViewChild(TableviewComponent)
+  tableViewComponent: TableviewComponent;
+
+  @ViewChild('lgSaveModal')
+  childSaveModal: ModalDirective;
+
+  @ViewChild(EditSearchFieldsComponent)
+  editSearchFieldsComponent: EditSearchFieldsComponent;
+
+  @ViewChild(OverwriteDialogComponent)
+  overwriteDialogComponent: OverwriteDialogComponent;
+
+  @ViewChild('advancedSearch')
+  advancedSearchPanel: AccordionPanelComponent;
+
+  @ViewChild('searchResults')
+  searchResultsPanel: AccordionPanelComponent;
+
+  constructor(private searchService: SearchService,
+    private queryService: QueryService,
+    private filterService: FilterService,
+    private nodeService: NodeService,
+    private localService: LocalizationService,
+    private notificationService: MDMNotificationService,
+    private basketService: BasketService) { }
+
+  ngOnInit() {
+    this.currentFilter = this.filterService.EMPTY_FILTER;
+
+    this.nodeService.getNodes()
+      .flatMap(envs => Observable.combineLatest([
+        Observable.of(envs),
+        this.searchService.loadSearchAttributesStructured(envs.map(env => env.sourceName)),
+        this.filterService.getFilters().defaultIfEmpty([this.currentFilter]),
+        this.searchService.getDefinitionsSimple()
+      ])).subscribe(
+        ([envs, attrs, filters, definitions]) => this.init(envs, attrs, filters, definitions),
+        error => this.notificationService.notifyError('Datenquellen können nicht geladen werden.', error)
+      );
+
+    // event handlers
+    this.viewComponent.viewChanged$.subscribe(
+      () => this.onViewChanged(),
+      error => this.notificationService.notifyError('Ansicht kann nicht aktualisiert werden.', error)
+    );
+  }
+
+  ngOnDestroy() {
+     this.saveState();
+  }
+
+  init(envs: Node[], attrs: { [type: string]: { [env: string]: SearchAttribute[] }}, filters: SearchFilter[], definitions: SearchDefinition[]) {
+    this.environments = envs
+    this.allSearchAttributes = attrs;
+    this.filters = filters;
+    this.definitions = definitions;
+
+    this.dropdownModel = envs.map(env => <SelectItem>{ value: env.sourceName, label: env.name });
+    this.selectedEnvs = envs.map(env => env.sourceName);
+
+    this.updateSearchAttributesForCurrentResultType();
+    this.selectedEnvironmentsChanged();
+
+    this.loadState();
+  }
+
+  loadState() {
+
+    this.results = deserialize(SearchResult, sessionStorage.getItem('mdm-search.searchResult')) || new SearchResult();
+    this.selectFilter(deserialize(SearchFilter, sessionStorage.getItem('mdm-search.currentFilter')) || this.filterService.EMPTY_FILTER);
+    this.isAdvancedSearchActive = !('false' == sessionStorage.getItem('mdm-search.isAdvancedSearchActive'));
+    this.advancedSearchPanel.isOpen = !('false' == sessionStorage.getItem('mdm-search.isAdvancedSearchOpen'));
+    this.searchResultsPanel.isOpen = !('false' == sessionStorage.getItem('mdm-search.isSearchResultsOpen'));
+  }
+
+  saveState() {
+    sessionStorage.setItem('mdm-search.isSearchResultsOpen', serialize(this.searchResultsPanel.isOpen));
+    sessionStorage.setItem('mdm-search.isAdvancedSearchOpen', serialize(this.advancedSearchPanel.isOpen));
+    sessionStorage.setItem('mdm-search.currentFilter', serialize(this.currentFilter));
+    sessionStorage.setItem('mdm-search.searchResult', serialize(this.results));
+    sessionStorage.setItem('mdm-search.isAdvancedSearchActive', this.isAdvancedSearchActive.toString());
+  }
+
+  onViewClick(e: Event) {
+    e.stopPropagation();
+  }
+
+  onCheckBoxClick(event: any) {
+    event.stopPropagation();
+    this.isAdvancedSearchActive = event.target.checked;
+  }
+
+  onViewChanged() {
+    if (this.searchExecuted) {
+      this.onSearch();
+    }
+  }
+
+  selectedEnvironmentsChanged() {
+    this.currentFilter.environments = this.selectedEnvs;
+    if (this.environments) {
+      let envs = this.environments.filter(env =>
+        this.currentFilter.environments.find(envName => envName === env.sourceName));
+
+      if (envs.length === 0) {
+        this.selectedEnvironments = this.environments;
+      } else {
+        this.selectedEnvironments = envs;
+      }
+    }
+    this.calcCurrentSearch();
+  }
+
+  loadFilters() {
+    this.filters = [];
+    this.filterService.getFilters()
+      .defaultIfEmpty([this.currentFilter])
+      .subscribe(
+        filters => this.filters = this.filters.concat(filters),
+        error => this.notificationService.notifyError('Suchfilter kann nicht geladen werden.', error)
+      );
+  }
+
+  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;
+    this.loading = true;
+    this.isSearchResultsOpen = true;
+    if (this.isAdvancedSearchActive) {
+      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;
+          this.loading = false;
+        },
+        error => this.notificationService.notifyError('Suchanfrage kann 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.getSelectedDefinition();
+    this.layout = SearchLayout.createSearchLayout(environments, this.allSearchAttributesForCurrentResultType, conditions);
+  }
+
+  selectFilter(filter: SearchFilter) {
+    this.currentFilter = classToClass(filter);
+    this.selectedEnvs = this.currentFilter.environments;
+    this.updateSearchAttributesForCurrentResultType();
+    this.selectedEnvironmentsChanged();
+    this.calcCurrentSearch();
+  }
+
+  resetConditions(e: Event) {
+    e.stopPropagation();
+    this.currentFilter.conditions.forEach(cond => cond.value = []);
+    this.selectFilter(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', '', []));
+        },
+        error => this.notificationService.notifyError('Filter kann nicht gelöscht werden', error)
+      );
+    }
+  }
+
+  saveFilter(e: Event) {
+    e.stopPropagation();
+    if (this.filters.find(f => f.name === this.filterName) !== undefined) {
+      this.childSaveModal.hide();
+      this.overwriteDialogComponent.showOverwriteModal('ein Filter').subscribe(
+        needSave => this.saveFilter2(needSave),
+        error => {
+          this.saveFilter2(false);
+          this.notificationService.notifyError('Suchfilter kann nicht gespeichert werden', error);
+        }
+      );
+    } 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);
+        },
+        error => this.notificationService.notifyError('Suchfilter kann nicht gespeichert werden', error)
+      );
+      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();
+      },
+      error => this.notificationService.notifyError('Suchfeldeditor kann nicht angezeigt werden.', error)
+    );
+  }
+
+  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.isAdvancedSearchActive ? 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..838d5a8
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/mdm-search.module.ts
@@ -0,0 +1,57 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..9a362ea
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-base.ts
@@ -0,0 +1,47 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..912fe2e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.css
@@ -0,0 +1,51 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..88a56dd
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.html
@@ -0,0 +1,45 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<td style="vertical-align: middle">{{condition.type | translate}}</td>

+<td style="vertical-align: middle">{{condition.type | translate: 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..0ca3090
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-condition.component.ts
@@ -0,0 +1,136 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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';

+

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

+

+@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,

+              private notificationService: MDMNotificationService) { }

+

+  ngAfterViewInit() {

+    if (this.primeAutoCompleteComponent) {

+      /* Workaround for missing onBlur handler in primeng version 2.x.

+       * We overwrite the existing implementation and additional call our own event handler.

+       * In later versions this feature was added https://github.com/primefaces/primeng/issues/2256

+       * and this workaround should be removed when using primeng version 4 or later

+       */

+      this.primeAutoCompleteComponent.onBlur = function() {

+        this.primeAutoCompleteComponent.focus = false;

+        this.primeAutoCompleteComponent.onModelTouched();

+        this.onAutocompleteBlur();

+      }.bind(this);

+    }

+  }

+

+  onAutocompleteBlur() {

+    this.onEnter(new Event("blur"));

+  }

+

+  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)),

+        error => this.notificationService.notifyError('Autotvervollständigung kann nicht initialisiert werden.', error)

+      );

+  }

+

+  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;

+  }

+

+  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..cef1485
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-control.service.ts
@@ -0,0 +1,33 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..4366acf
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-datepicker.component.html
@@ -0,0 +1,26 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..eff4fb2
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-datepicker.component.ts
@@ -0,0 +1,55 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..16abd3f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-dropdown.ts
@@ -0,0 +1,26 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..a49382f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search-textbox.ts
@@ -0,0 +1,26 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..b3509b2
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search.service.spec.ts
@@ -0,0 +1,318 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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 {HttpErrorHandler} from '../core/http-error-handler';

+

+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,

+        HttpErrorHandler,

+        {

+          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', 'Number', Operator.EQUALS, ['12']);

+      let cond3 = new Condition('Vehicle', 'Created', Operator.EQUALS, ['2017-07-17T12:13:14']);

+

+      let attributes = [

+          new SearchAttribute('env1', 'Test', 'Name'),

+          new SearchAttribute('env1', 'Vehicle', 'Number', 'LONG'),

+          new SearchAttribute('env2', 'Test', 'Name'),

+          new SearchAttribute('env2', 'Uut', 'Name'),

+          new SearchAttribute('env2', 'Vehicle', 'Created', 'DATE'),

+        ];

+

+      let filter = service.convertEnv('env1', [cond1, cond2, cond3], attributes, 'test');

+

+      expect(filter.sourceName).toEqual('env1');

+      expect(filter.filter).toEqual("Test.Name ci_lk 'PBN*' and Vehicle.Number eq 12 and Vehicle.Created eq '2017-07-17T12:13:14'");

+      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..f057e38
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/search/search.service.ts
@@ -0,0 +1,310 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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 {HttpErrorHandler} from '../core/http-error-handler';
+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 httpErrorHandler: HttpErrorHandler,
+    private localService: LocalizationService,
+    private _prop: PropertyService,
+    private preferenceService: PreferenceService,
+    private notificationService: MDMNotificationService) {
+      this.preferenceService.getPreference('ignoredAttributes').subscribe(
+        prefs => this.ignoreAttributesPrefs = this.ignoreAttributesPrefs.concat(prefs),
+        error => this.notificationService.notifyError('Einstellung für zu ignorirende Attribute kann nicht geladen werden.', error)
+      );
+    }
+
+  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))
+      .catch(this.httpErrorHandler.handleError);
+  }
+
+  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 + ' ' 
+         + this.adjustOperator(OperatorUtil.toFilterString(c.operator), this.getValueType(c, attrs)) + ' ' + this.quoteValue(value, this.getValueType(c, attrs))).join(' or '))
+      .filter(c => c.length > 0)
+      .join(' and ');
+
+    return new Filter(env, filterString, fullTextQuery);
+  }
+
+  quoteValue(value: string, valueType: string) {
+    if (valueType.toLowerCase() === 'string' || valueType.toLowerCase() === 'date') {
+      return "'" + value + "'";
+    } else {
+      return value;
+    }
+  }
+
+  getValueType(c: Condition, attrs: SearchAttribute[]) {
+    return attrs.find(a => a.boType == c.type && a.attrName == c.attribute).valueType;
+  }
+  
+  adjustOperator(operator: string, valueType: string) {
+    if (valueType.toLowerCase() === 'string') {
+      return "ci_" + operator;
+    } else {
+      return operator;
+    }
+  }
+
+  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..f53a601
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.component.html
@@ -0,0 +1,24 @@
+<!--********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************-->
+
+<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..2bb5762
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.component.ts
@@ -0,0 +1,125 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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 {TypeaheadMatch} from 'ngx-bootstrap/typeahead';
+import {ModalDirective} from 'ngx-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..3f900b2
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/searchattribute-tree/searchattribute-tree.module.ts
@@ -0,0 +1,36 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..75932bb
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/editview.component.html
@@ -0,0 +1,103 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..5c35db1
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/editview.component.ts
@@ -0,0 +1,191 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+
+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 'ngx-bootstrap';
+import {TypeaheadMatch} from 'ngx-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),

+      error => this.notificationService.notifyError('Knoten kann nicht ausgewählt werden.', error)

+    );

+

+    this.nodeService.getNodes()

+      .subscribe(

+        envs => {

+          this.searchService.loadSearchAttributesStructured(envs.map(e => e.sourceName))

+            .map(attrs => attrs['measurements'])

+            .subscribe(

+              attrs => this.refreshTypeAheadValues(attrs, envs),

+              error => this.notificationService.notifyError('Suchattribute können nicht geladen werden', error)

+            );

+        },

+        error => this.notificationService.notifyError('Knoten können nicht geladen werden', error)

+      );

+  }

+

+  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..aa84ed0
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/query.service.spec.ts
@@ -0,0 +1,105 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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 {HttpErrorHandler} from '../core/http-error-handler';

+

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

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

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

+

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

+

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        PropertyService,

+        QueryService,

+        MockBackend,

+        BaseRequestOptions,

+        HttpErrorHandler,

+        {

+          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: 'id1', 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('queryType()', () => {

+    it('should quote IDs', async(inject([QueryService], (queryService) => {

+      var spy = spyOn(queryService, 'query').and.returnValue(Observable.of(new SearchResult()));

+

+      var query = new Query();

+      query.resultType = 'TestStep';

+      query.addFilter('MDM', 'Test.Id eq \'id1\'');

+      query.columns = ['TestStep.Name', 'TestStep.Id'];

+

+      queryService.queryType('TestStep', [{ source: 'MDM', type: 'Test', id: 'id1'}], ['TestStep.Name']).subscribe(results => {

+        expect(queryService.query).toHaveBeenCalledWith(query);

+      });

+    })));

+  });

+

+  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: 'id1', columns: [{type: 'Test', attribute: 'Name', value: 'TestNumberOne'}]}

+          ]};

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

+        }

+        return;

+      });

+      let item = new MDMItem('MDMNVH', 'Test', 'id1');

+

+      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..4914191
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/query.service.ts
@@ -0,0 +1,143 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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 {HttpErrorHandler} from '../core/http-error-handler';

+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 httpErrorHandler: HttpErrorHandler,

+              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.httpErrorHandler.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)

+      .catch(this.httpErrorHandler.handleError);

+  }

+}

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..4afeedf
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.css
@@ -0,0 +1,34 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+.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..90664b8
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.html
@@ -0,0 +1,65 @@
+<!--********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************-->
+ 
+<style>
+:host >>> .fixtable { table-layout: auto;}
+</style>
+
+<div *ngIf="loading" style="text-align: center; margin: 1em;">
+    <div class="fa fa-spinner fa-pulse fa-fw"></div>
+    Suchergebnisse werden geladen
+</div>
+<div *ngIf="!loading" 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..ee9614b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.component.ts
@@ -0,0 +1,242 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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';

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

+

+@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[];

+  @Input() loading: true;

+  @Input() loadingIcon = 'fa-spinner';

+

+  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,

+              private notificationService: MDMNotificationService) {

+  }

+

+  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),

+      error => this.notificationService.notifyError('Knoten konnte nicht berechnet werden.', error)

+    );

+    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..7d05b27
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.module.ts
@@ -0,0 +1,48 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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..bbd1656
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.service.spec.ts
@@ -0,0 +1,91 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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';

+import {HttpErrorHandler} from '../core/http-error-handler';

+

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

+

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

+

+  beforeEach(() => {

+    TestBed.configureTestingModule({

+      imports: [HttpModule],

+      providers: [

+        ViewService,

+        PropertyService,

+        PreferenceService,

+        MockBackend,

+        BaseRequestOptions,

+        HttpErrorHandler,

+        MDMNotificationService,

+        {

+          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..22e735e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/tableview.service.ts
@@ -0,0 +1,130 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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';
+

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

+

+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,

+              private notificationService: MDMNotificationService) {

+  }

+

+  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),

+      error => this.notificationService.notifyError('Ansicht konnte nicht gespeichert werden', error)

+    );

+  }
+
+  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..181f4cf
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/view.component.html
@@ -0,0 +1,103 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+<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..3cd52ee
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/tableview/view.component.ts
@@ -0,0 +1,220 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+

+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 'ngx-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)

+  editViewComponent: EditViewComponent;

+

+  @ViewChild('lgSaveModal')

+  childSaveModal: ModalDirective;

+

+  @ViewChild(OverwriteDialogComponent)

+  overwriteDialogComponent: OverwriteDialogComponent;

+

+  constructor(private viewService: ViewService,

+              private notificationService: MDMNotificationService) {

+  }

+

+  ngOnInit() {

+    this.viewService.getViews().subscribe(

+      views => this.setViews(views),

+      error => this.notificationService.notifyError('Ansichten können nicht geladen werden.', error)

+    );

+    this.viewService.viewSaved$.subscribe(

+      view => this.onViewSaved(view),

+      error => this.notificationService.notifyError('Es ist ein Fehler beim Speichern der Ansicht aufgetreten.', error)

+    );

+    this.viewService.viewDeleted$.subscribe(

+      () => this.onDeleteView(),

+      error => this.notificationService.notifyError('Es ist ein Fehler beim Löschen der Ansicht aufgetreten.', error)

+    );

+  }

+

+  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,

+      error => this.notificationService.notifyError('Ansicht kann nicht ausgewählt werden.', error)

+    );

+  }

+

+  public newView(e: Event) {

+    e.stopPropagation();

+    this.editViewComponent.showDialog(new View()).subscribe(

+      v => this.selectedView = v,

+      error => this.notificationService.notifyError('Ansichtseditor kann nicht angezeigt werden.', error)

+    );

+  }

+

+  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);

+        }

+      },

+      error => this.notificationService.notifyError('Ansichten konnten nicht aktualisiert werden.', error)

+    );

+  }

+

+ private onViewSaved(view: View) {

+   this.viewService.getViews().subscribe(

+     prefViews => this.getGroupedView(prefViews),

+     error => this.notificationService.notifyError('Ansichten konnten nicht aktualisiert werden.', error)

+   );

+  }

+

+  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),

+      error => this.notificationService.notifyError('Ansichten konnten nicht aktualisiert werden.', error)

+    );

+  }

+

+  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(

+        needSave => this.saveView2(needSave),

+        error => {

+          this.saveView2(false);

+          this.notificationService.notifyError('Ansicht konnte nicht gespeichert werden.', error);

+        }

+      );

+    } 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();

+          },

+          error => this.notificationService.notifyError('Die Ansicht kann nicht gelöscht werden.', error))

+      );

+    } 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/famfamfam_icons/brick.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/brick.png
new file mode 100644
index 0000000..7851cf3
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/brick.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/brick_add.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/brick_add.png
new file mode 100644
index 0000000..fac186b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/brick_add.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/calendar.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/calendar.png
new file mode 100644
index 0000000..6589138
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/calendar.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/chart_curve.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/chart_curve.png
new file mode 100644
index 0000000..01e933a
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/chart_curve.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/chart_curve_go.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/chart_curve_go.png
new file mode 100644
index 0000000..ac9eda5
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/chart_curve_go.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/cog.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/cog.png
new file mode 100644
index 0000000..67de2c6
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/cog.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/cog_go.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/cog_go.png
new file mode 100644
index 0000000..3262767
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/cog_go.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/computer.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/computer.png
new file mode 100644
index 0000000..9bc37dc
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/computer.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/database.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/database.png
new file mode 100644
index 0000000..3d09261
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/database.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/folder.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/folder.png
new file mode 100644
index 0000000..784e8fa
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/folder.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/house.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/house.png
new file mode 100644
index 0000000..fed6221
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/house.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/monitor.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/monitor.png
new file mode 100644
index 0000000..d040bd0
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/monitor.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/page_white_stack.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/page_white_stack.png
new file mode 100644
index 0000000..44084ad
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/page_white_stack.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/page_white_text.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/page_white_text.png
new file mode 100644
index 0000000..813f712
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/page_white_text.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/paste_plain.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/paste_plain.png
new file mode 100644
index 0000000..c0490eb
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/paste_plain.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/readme.html b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/readme.html
new file mode 100644
index 0000000..3852c6d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/readme.html
@@ -0,0 +1,1495 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

+

+	<!-- Document HEAD -->

+	<head>

+	

+		<title>famfamfam.com: Silk Icons</title>

+

+		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

+

+		<meta name="mssmarttagspreventparsing" content="true" />

+		<meta name="robots" content="all" />		

+		<meta http-equiv="imagetoolbar" content="no" />

+

+		<meta name="author" content="Mark James" />

+		<meta name="copyright" content="Mark James" />

+		<meta name="revisit-after" content="7 days" />		

+		

+	    <style type="text/css">

+<!--

+body {

+	font-family: Verdana, Arial, Helvetica, sans-serif;

+	font-size: 85%;

+}

+

+h1,h2,h3 {

+padding: 10px;

+background-color: #F0F0F0;

+border-bottom: 1px solid #DDD;

+}

+

+table td {

+padding: 5px;

+}

+

+td em {

+display: none;

+}

+-->

+        </style>

+	</head>

+

+	<!-- Document BODY -->

+	<body>

+

+		<div id="container-outer">

+			<div id="container-inner">

+					<div id="content-inner">				

+

+						<h2>Silk Icons</h2>

+						<p><a href="http://www.famfamfam.com/lab/icons/silk/">http://www.famfamfam.com/lab/icons/silk/</a></p>

+					  <p>

+							&#8220;Silk&#8221; is a smooth icon set, containing 1000 16-by-16 pixel icons in

+							strokably-soft PNG format. Containing a large variety of icons, you're sure to find something

+							that tickles your fancy. And all for a low low price of $0.00. You can't say fairer than that.

+							

+							<br />

+							<br/>

+

+						  <!--

+							<a class="download-link" href="famfamfam_mini_icons.zip">Download</a><br />

+							-->

+							

+						  Current version: <strong>1.3</strong></p>

+						

+						<h3>License</h3>

+						<p>

+						<!-- Creative Commons License -->

+						I also love to hear of my work being used, feel encouraged to send an email

+						with a link or screenshot of the icons in their new home to

+						mjames&nbsp;<span class="email">at</span>&nbsp;gmail&nbsp;<span class="email">dot</span>&nbsp;com.

+						This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/2.5/">Creative Commons Attribution 2.5 License</a>.

+						This means you may use it for any purpose, and make any changes you like.

+						All I ask is that you include a link back to <a href="http://www.famfamfam.com/lab/icons/silk/">http://www.famfamfam.com/lab/icons/silk/</a> in your credits (contact me to discuss licencing further).

+						<!-- /Creative Commons License -->

+

+						

+						<!--

+						

+						<rdf:RDF xmlns="http://web.resource.org/cc/"

+							xmlns:dc="http://purl.org/dc/elements/1.1/"

+							xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">

+						<Work rdf:about="">

+						   <dc:title>Silk icons</dc:title>

+						   <dc:date>2005</dc:date>

+						   <dc:description>'Silk' icon set.</dc:description>

+						   <dc:creator><Agent>

+							  <dc:title>Mark James</dc:title>

+						   </Agent></dc:creator>

+						   <dc:rights><Agent>

+							  <dc:title>Mark James</dc:title>

+						   </Agent></dc:rights>

+						   <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />

+						   <dc:source rdf:resource="http://www.famfamfam.com/lab/icons/silk/"/>

+						   <license rdf:resource="http://creativecommons.org/licenses/by/2.5/" />

+						</Work>

+						

+						<License rdf:about="http://creativecommons.org/licenses/by/2.5/">

+						   <permits rdf:resource="http://web.resource.org/cc/Reproduction" />

+						   <permits rdf:resource="http://web.resource.org/cc/Distribution" />

+						   <requires rdf:resource="http://web.resource.org/cc/Notice" />

+						   <requires rdf:resource="http://web.resource.org/cc/Attribution" />

+						   <permits rdf:resource="http://web.resource.org/cc/DerivativeWorks" />

+						</License>

+						

+						</rdf:RDF>

+						

+						-->

+						</p>

+						

+						<h3>Sightings</h3>

+						

+						<p>

+						Do you use this set? <a href="http://www.famfamfam.com/about/?contact=Silk%20icons%20in%20the%20wild">Contact me!</a>.

+						</p>

+						

+						

+						<h3>Icons</h3>

+

+						

+						<p><table>

+	<tr>

+		<td><img src="icons/accept.png" title="accept.png" alt="accept "/><em>accept.png</em></td>

+		<td><img src="icons/add.png" title="add.png" alt="add "/><em>add.png</em></td>

+

+		<td><img src="icons/anchor.png" title="anchor.png" alt="anchor "/><em>anchor.png</em></td>

+		<td><img src="icons/application.png" title="application.png" alt="application "/><em>application.png</em></td>

+		<td><img src="icons/application_add.png" title="application_add.png" alt="application add "/><em>application_add.png</em></td>

+		<td><img src="icons/application_cascade.png" title="application_cascade.png" alt="application cascade "/><em>application_cascade.png</em></td>

+		<td><img src="icons/application_delete.png" title="application_delete.png" alt="application delete "/><em>application_delete.png</em></td>

+		<td><img src="icons/application_double.png" title="application_double.png" alt="application double "/><em>application_double.png</em></td>

+

+		<td><img src="icons/application_edit.png" title="application_edit.png" alt="application edit "/><em>application_edit.png</em></td>

+		<td><img src="icons/application_error.png" title="application_error.png" alt="application error "/><em>application_error.png</em></td>

+		<td><img src="icons/application_form.png" title="application_form.png" alt="application form "/><em>application_form.png</em></td>

+		<td><img src="icons/application_form_add.png" title="application_form_add.png" alt="application form add "/><em>application_form_add.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/application_form_delete.png" title="application_form_delete.png" alt="application form delete "/><em>application_form_delete.png</em></td>

+

+		<td><img src="icons/application_form_edit.png" title="application_form_edit.png" alt="application form edit "/><em>application_form_edit.png</em></td>

+		<td><img src="icons/application_form_magnify.png" title="application_form_magnify.png" alt="application form magnify "/><em>application_form_magnify.png</em></td>

+		<td><img src="icons/application_get.png" title="application_get.png" alt="application get "/><em>application_get.png</em></td>

+		<td><img src="icons/application_go.png" title="application_go.png" alt="application go "/><em>application_go.png</em></td>

+		<td><img src="icons/application_home.png" title="application_home.png" alt="application home "/><em>application_home.png</em></td>

+		<td><img src="icons/application_key.png" title="application_key.png" alt="application key "/><em>application_key.png</em></td>

+

+		<td><img src="icons/application_lightning.png" title="application_lightning.png" alt="application lightning "/><em>application_lightning.png</em></td>

+		<td><img src="icons/application_link.png" title="application_link.png" alt="application link "/><em>application_link.png</em></td>

+		<td><img src="icons/application_osx.png" title="application_osx.png" alt="application osx "/><em>application_osx.png</em></td>

+		<td><img src="icons/application_osx_terminal.png" title="application_osx_terminal.png" alt="application osx terminal "/><em>application_osx_terminal.png</em></td>

+		<td><img src="icons/application_put.png" title="application_put.png" alt="application put "/><em>application_put.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/application_side_boxes.png" title="application_side_boxes.png" alt="application side boxes "/><em>application_side_boxes.png</em></td>

+		<td><img src="icons/application_side_contract.png" title="application_side_contract.png" alt="application side contract "/><em>application_side_contract.png</em></td>

+		<td><img src="icons/application_side_expand.png" title="application_side_expand.png" alt="application side expand "/><em>application_side_expand.png</em></td>

+		<td><img src="icons/application_side_list.png" title="application_side_list.png" alt="application side list "/><em>application_side_list.png</em></td>

+		<td><img src="icons/application_side_tree.png" title="application_side_tree.png" alt="application side tree "/><em>application_side_tree.png</em></td>

+

+		<td><img src="icons/application_split.png" title="application_split.png" alt="application split "/><em>application_split.png</em></td>

+		<td><img src="icons/application_tile_horizontal.png" title="application_tile_horizontal.png" alt="application tile horizontal "/><em>application_tile_horizontal.png</em></td>

+		<td><img src="icons/application_tile_vertical.png" title="application_tile_vertical.png" alt="application tile vertical "/><em>application_tile_vertical.png</em></td>

+		<td><img src="icons/application_view_columns.png" title="application_view_columns.png" alt="application view columns "/><em>application_view_columns.png</em></td>

+		<td><img src="icons/application_view_detail.png" title="application_view_detail.png" alt="application view detail "/><em>application_view_detail.png</em></td>

+		<td><img src="icons/application_view_gallery.png" title="application_view_gallery.png" alt="application view gallery "/><em>application_view_gallery.png</em></td>

+

+		<td><img src="icons/application_view_icons.png" title="application_view_icons.png" alt="application view icons "/><em>application_view_icons.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/application_view_list.png" title="application_view_list.png" alt="application view list "/><em>application_view_list.png</em></td>

+		<td><img src="icons/application_view_tile.png" title="application_view_tile.png" alt="application view tile "/><em>application_view_tile.png</em></td>

+		<td><img src="icons/application_xp.png" title="application_xp.png" alt="application xp "/><em>application_xp.png</em></td>

+		<td><img src="icons/application_xp_terminal.png" title="application_xp_terminal.png" alt="application xp terminal "/><em>application_xp_terminal.png</em></td>

+

+		<td><img src="icons/arrow_branch.png" title="arrow_branch.png" alt="arrow branch "/><em>arrow_branch.png</em></td>

+		<td><img src="icons/arrow_divide.png" title="arrow_divide.png" alt="arrow divide "/><em>arrow_divide.png</em></td>

+		<td><img src="icons/arrow_down.png" title="arrow_down.png" alt="arrow down "/><em>arrow_down.png</em></td>

+		<td><img src="icons/arrow_in.png" title="arrow_in.png" alt="arrow in "/><em>arrow_in.png</em></td>

+		<td><img src="icons/arrow_inout.png" title="arrow_inout.png" alt="arrow inout "/><em>arrow_inout.png</em></td>

+		<td><img src="icons/arrow_join.png" title="arrow_join.png" alt="arrow join "/><em>arrow_join.png</em></td>

+

+		<td><img src="icons/arrow_left.png" title="arrow_left.png" alt="arrow left "/><em>arrow_left.png</em></td>

+		<td><img src="icons/arrow_merge.png" title="arrow_merge.png" alt="arrow merge "/><em>arrow_merge.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/arrow_out.png" title="arrow_out.png" alt="arrow out "/><em>arrow_out.png</em></td>

+		<td><img src="icons/arrow_redo.png" title="arrow_redo.png" alt="arrow redo "/><em>arrow_redo.png</em></td>

+		<td><img src="icons/arrow_refresh.png" title="arrow_refresh.png" alt="arrow refresh "/><em>arrow_refresh.png</em></td>

+

+		<td><img src="icons/arrow_refresh_small.png" title="arrow_refresh_small.png" alt="arrow refresh small "/><em>arrow_refresh_small.png</em></td>

+		<td><img src="icons/arrow_right.png" title="arrow_right.png" alt="arrow right "/><em>arrow_right.png</em></td>

+		<td><img src="icons/arrow_rotate_anticlockwise.png" title="arrow_rotate_anticlockwise.png" alt="arrow rotate anticlockwise "/><em>arrow_rotate_anticlockwise.png</em></td>

+		<td><img src="icons/arrow_rotate_clockwise.png" title="arrow_rotate_clockwise.png" alt="arrow rotate clockwise "/><em>arrow_rotate_clockwise.png</em></td>

+		<td><img src="icons/arrow_switch.png" title="arrow_switch.png" alt="arrow switch "/><em>arrow_switch.png</em></td>

+		<td><img src="icons/arrow_turn_left.png" title="arrow_turn_left.png" alt="arrow turn left "/><em>arrow_turn_left.png</em></td>

+

+		<td><img src="icons/arrow_turn_right.png" title="arrow_turn_right.png" alt="arrow turn right "/><em>arrow_turn_right.png</em></td>

+		<td><img src="icons/arrow_undo.png" title="arrow_undo.png" alt="arrow undo "/><em>arrow_undo.png</em></td>

+		<td><img src="icons/arrow_up.png" title="arrow_up.png" alt="arrow up "/><em>arrow_up.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/asterisk_orange.png" title="asterisk_orange.png" alt="asterisk orange "/><em>asterisk_orange.png</em></td>

+		<td><img src="icons/asterisk_yellow.png" title="asterisk_yellow.png" alt="asterisk yellow "/><em>asterisk_yellow.png</em></td>

+

+		<td><img src="icons/attach.png" title="attach.png" alt="attach "/><em>attach.png</em></td>

+		<td><img src="icons/award_star_add.png" title="award_star_add.png" alt="award star add "/><em>award_star_add.png</em></td>

+		<td><img src="icons/award_star_bronze_1.png" title="award_star_bronze_1.png" alt="award star bronze 1 "/><em>award_star_bronze_1.png</em></td>

+		<td><img src="icons/award_star_bronze_2.png" title="award_star_bronze_2.png" alt="award star bronze 2 "/><em>award_star_bronze_2.png</em></td>

+		<td><img src="icons/award_star_bronze_3.png" title="award_star_bronze_3.png" alt="award star bronze 3 "/><em>award_star_bronze_3.png</em></td>

+		<td><img src="icons/award_star_delete.png" title="award_star_delete.png" alt="award star delete "/><em>award_star_delete.png</em></td>

+

+		<td><img src="icons/award_star_gold_1.png" title="award_star_gold_1.png" alt="award star gold 1 "/><em>award_star_gold_1.png</em></td>

+		<td><img src="icons/award_star_gold_2.png" title="award_star_gold_2.png" alt="award star gold 2 "/><em>award_star_gold_2.png</em></td>

+		<td><img src="icons/award_star_gold_3.png" title="award_star_gold_3.png" alt="award star gold 3 "/><em>award_star_gold_3.png</em></td>

+		<td><img src="icons/award_star_silver_1.png" title="award_star_silver_1.png" alt="award star silver 1 "/><em>award_star_silver_1.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/award_star_silver_2.png" title="award_star_silver_2.png" alt="award star silver 2 "/><em>award_star_silver_2.png</em></td>

+

+		<td><img src="icons/award_star_silver_3.png" title="award_star_silver_3.png" alt="award star silver 3 "/><em>award_star_silver_3.png</em></td>

+		<td><img src="icons/basket.png" title="basket.png" alt="basket "/><em>basket.png</em></td>

+		<td><img src="icons/basket_add.png" title="basket_add.png" alt="basket add "/><em>basket_add.png</em></td>

+		<td><img src="icons/basket_delete.png" title="basket_delete.png" alt="basket delete "/><em>basket_delete.png</em></td>

+		<td><img src="icons/basket_edit.png" title="basket_edit.png" alt="basket edit "/><em>basket_edit.png</em></td>

+		<td><img src="icons/basket_error.png" title="basket_error.png" alt="basket error "/><em>basket_error.png</em></td>

+

+		<td><img src="icons/basket_go.png" title="basket_go.png" alt="basket go "/><em>basket_go.png</em></td>

+		<td><img src="icons/basket_put.png" title="basket_put.png" alt="basket put "/><em>basket_put.png</em></td>

+		<td><img src="icons/basket_remove.png" title="basket_remove.png" alt="basket remove "/><em>basket_remove.png</em></td>

+		<td><img src="icons/bell.png" title="bell.png" alt="bell "/><em>bell.png</em></td>

+		<td><img src="icons/bell_add.png" title="bell_add.png" alt="bell add "/><em>bell_add.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/bell_delete.png" title="bell_delete.png" alt="bell delete "/><em>bell_delete.png</em></td>

+		<td><img src="icons/bell_error.png" title="bell_error.png" alt="bell error "/><em>bell_error.png</em></td>

+		<td><img src="icons/bell_go.png" title="bell_go.png" alt="bell go "/><em>bell_go.png</em></td>

+		<td><img src="icons/bell_link.png" title="bell_link.png" alt="bell link "/><em>bell_link.png</em></td>

+		<td><img src="icons/bin.png" title="bin.png" alt="bin "/><em>bin.png</em></td>

+

+		<td><img src="icons/bin_closed.png" title="bin_closed.png" alt="bin closed "/><em>bin_closed.png</em></td>

+		<td><img src="icons/bin_empty.png" title="bin_empty.png" alt="bin empty "/><em>bin_empty.png</em></td>

+		<td><img src="icons/bomb.png" title="bomb.png" alt="bomb "/><em>bomb.png</em></td>

+		<td><img src="icons/book.png" title="book.png" alt="book "/><em>book.png</em></td>

+		<td><img src="icons/book_add.png" title="book_add.png" alt="book add "/><em>book_add.png</em></td>

+		<td><img src="icons/book_addresses.png" title="book_addresses.png" alt="book addresses "/><em>book_addresses.png</em></td>

+

+		<td><img src="icons/book_delete.png" title="book_delete.png" alt="book delete "/><em>book_delete.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/book_edit.png" title="book_edit.png" alt="book edit "/><em>book_edit.png</em></td>

+		<td><img src="icons/book_error.png" title="book_error.png" alt="book error "/><em>book_error.png</em></td>

+		<td><img src="icons/book_go.png" title="book_go.png" alt="book go "/><em>book_go.png</em></td>

+		<td><img src="icons/book_key.png" title="book_key.png" alt="book key "/><em>book_key.png</em></td>

+

+		<td><img src="icons/book_link.png" title="book_link.png" alt="book link "/><em>book_link.png</em></td>

+		<td><img src="icons/book_next.png" title="book_next.png" alt="book next "/><em>book_next.png</em></td>

+		<td><img src="icons/book_open.png" title="book_open.png" alt="book open "/><em>book_open.png</em></td>

+		<td><img src="icons/book_previous.png" title="book_previous.png" alt="book previous "/><em>book_previous.png</em></td>

+		<td><img src="icons/box.png" title="box.png" alt="box "/><em>box.png</em></td>

+		<td><img src="icons/brick.png" title="brick.png" alt="brick "/><em>brick.png</em></td>

+

+		<td><img src="icons/brick_add.png" title="brick_add.png" alt="brick add "/><em>brick_add.png</em></td>

+		<td><img src="icons/brick_delete.png" title="brick_delete.png" alt="brick delete "/><em>brick_delete.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/brick_edit.png" title="brick_edit.png" alt="brick edit "/><em>brick_edit.png</em></td>

+		<td><img src="icons/brick_error.png" title="brick_error.png" alt="brick error "/><em>brick_error.png</em></td>

+		<td><img src="icons/brick_go.png" title="brick_go.png" alt="brick go "/><em>brick_go.png</em></td>

+

+		<td><img src="icons/brick_link.png" title="brick_link.png" alt="brick link "/><em>brick_link.png</em></td>

+		<td><img src="icons/bricks.png" title="bricks.png" alt="bricks "/><em>bricks.png</em></td>

+		<td><img src="icons/briefcase.png" title="briefcase.png" alt="briefcase "/><em>briefcase.png</em></td>

+		<td><img src="icons/bug.png" title="bug.png" alt="bug "/><em>bug.png</em></td>

+		<td><img src="icons/bug_add.png" title="bug_add.png" alt="bug add "/><em>bug_add.png</em></td>

+		<td><img src="icons/bug_delete.png" title="bug_delete.png" alt="bug delete "/><em>bug_delete.png</em></td>

+

+		<td><img src="icons/bug_edit.png" title="bug_edit.png" alt="bug edit "/><em>bug_edit.png</em></td>

+		<td><img src="icons/bug_error.png" title="bug_error.png" alt="bug error "/><em>bug_error.png</em></td>

+		<td><img src="icons/bug_go.png" title="bug_go.png" alt="bug go "/><em>bug_go.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/bug_link.png" title="bug_link.png" alt="bug link "/><em>bug_link.png</em></td>

+		<td><img src="icons/building.png" title="building.png" alt="building "/><em>building.png</em></td>

+

+		<td><img src="icons/building_add.png" title="building_add.png" alt="building add "/><em>building_add.png</em></td>

+		<td><img src="icons/building_delete.png" title="building_delete.png" alt="building delete "/><em>building_delete.png</em></td>

+		<td><img src="icons/building_edit.png" title="building_edit.png" alt="building edit "/><em>building_edit.png</em></td>

+		<td><img src="icons/building_error.png" title="building_error.png" alt="building error "/><em>building_error.png</em></td>

+		<td><img src="icons/building_go.png" title="building_go.png" alt="building go "/><em>building_go.png</em></td>

+		<td><img src="icons/building_key.png" title="building_key.png" alt="building key "/><em>building_key.png</em></td>

+

+		<td><img src="icons/building_link.png" title="building_link.png" alt="building link "/><em>building_link.png</em></td>

+		<td><img src="icons/bullet_add.png" title="bullet_add.png" alt="bullet add "/><em>bullet_add.png</em></td>

+		<td><img src="icons/bullet_arrow_bottom.png" title="bullet_arrow_bottom.png" alt="bullet arrow bottom "/><em>bullet_arrow_bottom.png</em></td>

+		<td><img src="icons/bullet_arrow_down.png" title="bullet_arrow_down.png" alt="bullet arrow down "/><em>bullet_arrow_down.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/bullet_arrow_top.png" title="bullet_arrow_top.png" alt="bullet arrow top "/><em>bullet_arrow_top.png</em></td>

+

+		<td><img src="icons/bullet_arrow_up.png" title="bullet_arrow_up.png" alt="bullet arrow up "/><em>bullet_arrow_up.png</em></td>

+		<td><img src="icons/bullet_black.png" title="bullet_black.png" alt="bullet black "/><em>bullet_black.png</em></td>

+		<td><img src="icons/bullet_blue.png" title="bullet_blue.png" alt="bullet blue "/><em>bullet_blue.png</em></td>

+		<td><img src="icons/bullet_delete.png" title="bullet_delete.png" alt="bullet delete "/><em>bullet_delete.png</em></td>

+		<td><img src="icons/bullet_disk.png" title="bullet_disk.png" alt="bullet disk "/><em>bullet_disk.png</em></td>

+		<td><img src="icons/bullet_error.png" title="bullet_error.png" alt="bullet error "/><em>bullet_error.png</em></td>

+

+		<td><img src="icons/bullet_feed.png" title="bullet_feed.png" alt="bullet feed "/><em>bullet_feed.png</em></td>

+		<td><img src="icons/bullet_go.png" title="bullet_go.png" alt="bullet go "/><em>bullet_go.png</em></td>

+		<td><img src="icons/bullet_green.png" title="bullet_green.png" alt="bullet green "/><em>bullet_green.png</em></td>

+		<td><img src="icons/bullet_key.png" title="bullet_key.png" alt="bullet key "/><em>bullet_key.png</em></td>

+		<td><img src="icons/bullet_orange.png" title="bullet_orange.png" alt="bullet orange "/><em>bullet_orange.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/bullet_picture.png" title="bullet_picture.png" alt="bullet picture "/><em>bullet_picture.png</em></td>

+		<td><img src="icons/bullet_pink.png" title="bullet_pink.png" alt="bullet pink "/><em>bullet_pink.png</em></td>

+		<td><img src="icons/bullet_purple.png" title="bullet_purple.png" alt="bullet purple "/><em>bullet_purple.png</em></td>

+		<td><img src="icons/bullet_red.png" title="bullet_red.png" alt="bullet red "/><em>bullet_red.png</em></td>

+		<td><img src="icons/bullet_star.png" title="bullet_star.png" alt="bullet star "/><em>bullet_star.png</em></td>

+

+		<td><img src="icons/bullet_toggle_minus.png" title="bullet_toggle_minus.png" alt="bullet toggle minus "/><em>bullet_toggle_minus.png</em></td>

+		<td><img src="icons/bullet_toggle_plus.png" title="bullet_toggle_plus.png" alt="bullet toggle plus "/><em>bullet_toggle_plus.png</em></td>

+		<td><img src="icons/bullet_white.png" title="bullet_white.png" alt="bullet white "/><em>bullet_white.png</em></td>

+		<td><img src="icons/bullet_wrench.png" title="bullet_wrench.png" alt="bullet wrench "/><em>bullet_wrench.png</em></td>

+		<td><img src="icons/bullet_yellow.png" title="bullet_yellow.png" alt="bullet yellow "/><em>bullet_yellow.png</em></td>

+		<td><img src="icons/cake.png" title="cake.png" alt="cake "/><em>cake.png</em></td>

+

+		<td><img src="icons/calculator.png" title="calculator.png" alt="calculator "/><em>calculator.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/calculator_add.png" title="calculator_add.png" alt="calculator add "/><em>calculator_add.png</em></td>

+		<td><img src="icons/calculator_delete.png" title="calculator_delete.png" alt="calculator delete "/><em>calculator_delete.png</em></td>

+		<td><img src="icons/calculator_edit.png" title="calculator_edit.png" alt="calculator edit "/><em>calculator_edit.png</em></td>

+		<td><img src="icons/calculator_error.png" title="calculator_error.png" alt="calculator error "/><em>calculator_error.png</em></td>

+

+		<td><img src="icons/calculator_link.png" title="calculator_link.png" alt="calculator link "/><em>calculator_link.png</em></td>

+		<td><img src="icons/calendar.png" title="calendar.png" alt="calendar "/><em>calendar.png</em></td>

+		<td><img src="icons/calendar_add.png" title="calendar_add.png" alt="calendar add "/><em>calendar_add.png</em></td>

+		<td><img src="icons/calendar_delete.png" title="calendar_delete.png" alt="calendar delete "/><em>calendar_delete.png</em></td>

+		<td><img src="icons/calendar_edit.png" title="calendar_edit.png" alt="calendar edit "/><em>calendar_edit.png</em></td>

+		<td><img src="icons/calendar_link.png" title="calendar_link.png" alt="calendar link "/><em>calendar_link.png</em></td>

+

+		<td><img src="icons/calendar_view_day.png" title="calendar_view_day.png" alt="calendar view day "/><em>calendar_view_day.png</em></td>

+		<td><img src="icons/calendar_view_month.png" title="calendar_view_month.png" alt="calendar view month "/><em>calendar_view_month.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/calendar_view_week.png" title="calendar_view_week.png" alt="calendar view week "/><em>calendar_view_week.png</em></td>

+		<td><img src="icons/camera.png" title="camera.png" alt="camera "/><em>camera.png</em></td>

+		<td><img src="icons/camera_add.png" title="camera_add.png" alt="camera add "/><em>camera_add.png</em></td>

+

+		<td><img src="icons/camera_delete.png" title="camera_delete.png" alt="camera delete "/><em>camera_delete.png</em></td>

+		<td><img src="icons/camera_edit.png" title="camera_edit.png" alt="camera edit "/><em>camera_edit.png</em></td>

+		<td><img src="icons/camera_error.png" title="camera_error.png" alt="camera error "/><em>camera_error.png</em></td>

+		<td><img src="icons/camera_go.png" title="camera_go.png" alt="camera go "/><em>camera_go.png</em></td>

+		<td><img src="icons/camera_link.png" title="camera_link.png" alt="camera link "/><em>camera_link.png</em></td>

+		<td><img src="icons/camera_small.png" title="camera_small.png" alt="camera small "/><em>camera_small.png</em></td>

+

+		<td><img src="icons/cancel.png" title="cancel.png" alt="cancel "/><em>cancel.png</em></td>

+		<td><img src="icons/car.png" title="car.png" alt="car "/><em>car.png</em></td>

+		<td><img src="icons/car_add.png" title="car_add.png" alt="car add "/><em>car_add.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/car_delete.png" title="car_delete.png" alt="car delete "/><em>car_delete.png</em></td>

+		<td><img src="icons/cart.png" title="cart.png" alt="cart "/><em>cart.png</em></td>

+

+		<td><img src="icons/cart_add.png" title="cart_add.png" alt="cart add "/><em>cart_add.png</em></td>

+		<td><img src="icons/cart_delete.png" title="cart_delete.png" alt="cart delete "/><em>cart_delete.png</em></td>

+		<td><img src="icons/cart_edit.png" title="cart_edit.png" alt="cart edit "/><em>cart_edit.png</em></td>

+		<td><img src="icons/cart_error.png" title="cart_error.png" alt="cart error "/><em>cart_error.png</em></td>

+		<td><img src="icons/cart_go.png" title="cart_go.png" alt="cart go "/><em>cart_go.png</em></td>

+		<td><img src="icons/cart_put.png" title="cart_put.png" alt="cart put "/><em>cart_put.png</em></td>

+

+		<td><img src="icons/cart_remove.png" title="cart_remove.png" alt="cart remove "/><em>cart_remove.png</em></td>

+		<td><img src="icons/cd.png" title="cd.png" alt="cd "/><em>cd.png</em></td>

+		<td><img src="icons/cd_add.png" title="cd_add.png" alt="cd add "/><em>cd_add.png</em></td>

+		<td><img src="icons/cd_burn.png" title="cd_burn.png" alt="cd burn "/><em>cd_burn.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/cd_delete.png" title="cd_delete.png" alt="cd delete "/><em>cd_delete.png</em></td>

+

+		<td><img src="icons/cd_edit.png" title="cd_edit.png" alt="cd edit "/><em>cd_edit.png</em></td>

+		<td><img src="icons/cd_eject.png" title="cd_eject.png" alt="cd eject "/><em>cd_eject.png</em></td>

+		<td><img src="icons/cd_go.png" title="cd_go.png" alt="cd go "/><em>cd_go.png</em></td>

+		<td><img src="icons/chart_bar.png" title="chart_bar.png" alt="chart bar "/><em>chart_bar.png</em></td>

+		<td><img src="icons/chart_bar_add.png" title="chart_bar_add.png" alt="chart bar add "/><em>chart_bar_add.png</em></td>

+		<td><img src="icons/chart_bar_delete.png" title="chart_bar_delete.png" alt="chart bar delete "/><em>chart_bar_delete.png</em></td>

+

+		<td><img src="icons/chart_bar_edit.png" title="chart_bar_edit.png" alt="chart bar edit "/><em>chart_bar_edit.png</em></td>

+		<td><img src="icons/chart_bar_error.png" title="chart_bar_error.png" alt="chart bar error "/><em>chart_bar_error.png</em></td>

+		<td><img src="icons/chart_bar_link.png" title="chart_bar_link.png" alt="chart bar link "/><em>chart_bar_link.png</em></td>

+		<td><img src="icons/chart_curve.png" title="chart_curve.png" alt="chart curve "/><em>chart_curve.png</em></td>

+		<td><img src="icons/chart_curve_add.png" title="chart_curve_add.png" alt="chart curve add "/><em>chart_curve_add.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/chart_curve_delete.png" title="chart_curve_delete.png" alt="chart curve delete "/><em>chart_curve_delete.png</em></td>

+		<td><img src="icons/chart_curve_edit.png" title="chart_curve_edit.png" alt="chart curve edit "/><em>chart_curve_edit.png</em></td>

+		<td><img src="icons/chart_curve_error.png" title="chart_curve_error.png" alt="chart curve error "/><em>chart_curve_error.png</em></td>

+		<td><img src="icons/chart_curve_go.png" title="chart_curve_go.png" alt="chart curve go "/><em>chart_curve_go.png</em></td>

+		<td><img src="icons/chart_curve_link.png" title="chart_curve_link.png" alt="chart curve link "/><em>chart_curve_link.png</em></td>

+

+		<td><img src="icons/chart_line.png" title="chart_line.png" alt="chart line "/><em>chart_line.png</em></td>

+		<td><img src="icons/chart_line_add.png" title="chart_line_add.png" alt="chart line add "/><em>chart_line_add.png</em></td>

+		<td><img src="icons/chart_line_delete.png" title="chart_line_delete.png" alt="chart line delete "/><em>chart_line_delete.png</em></td>

+		<td><img src="icons/chart_line_edit.png" title="chart_line_edit.png" alt="chart line edit "/><em>chart_line_edit.png</em></td>

+		<td><img src="icons/chart_line_error.png" title="chart_line_error.png" alt="chart line error "/><em>chart_line_error.png</em></td>

+		<td><img src="icons/chart_line_link.png" title="chart_line_link.png" alt="chart line link "/><em>chart_line_link.png</em></td>

+

+		<td><img src="icons/chart_organisation.png" title="chart_organisation.png" alt="chart organisation "/><em>chart_organisation.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/chart_organisation_add.png" title="chart_organisation_add.png" alt="chart organisation add "/><em>chart_organisation_add.png</em></td>

+		<td><img src="icons/chart_organisation_delete.png" title="chart_organisation_delete.png" alt="chart organisation delete "/><em>chart_organisation_delete.png</em></td>

+		<td><img src="icons/chart_pie.png" title="chart_pie.png" alt="chart pie "/><em>chart_pie.png</em></td>

+		<td><img src="icons/chart_pie_add.png" title="chart_pie_add.png" alt="chart pie add "/><em>chart_pie_add.png</em></td>

+

+		<td><img src="icons/chart_pie_delete.png" title="chart_pie_delete.png" alt="chart pie delete "/><em>chart_pie_delete.png</em></td>

+		<td><img src="icons/chart_pie_edit.png" title="chart_pie_edit.png" alt="chart pie edit "/><em>chart_pie_edit.png</em></td>

+		<td><img src="icons/chart_pie_error.png" title="chart_pie_error.png" alt="chart pie error "/><em>chart_pie_error.png</em></td>

+		<td><img src="icons/chart_pie_link.png" title="chart_pie_link.png" alt="chart pie link "/><em>chart_pie_link.png</em></td>

+		<td><img src="icons/clock.png" title="clock.png" alt="clock "/><em>clock.png</em></td>

+		<td><img src="icons/clock_add.png" title="clock_add.png" alt="clock add "/><em>clock_add.png</em></td>

+

+		<td><img src="icons/clock_delete.png" title="clock_delete.png" alt="clock delete "/><em>clock_delete.png</em></td>

+		<td><img src="icons/clock_edit.png" title="clock_edit.png" alt="clock edit "/><em>clock_edit.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/clock_error.png" title="clock_error.png" alt="clock error "/><em>clock_error.png</em></td>

+		<td><img src="icons/clock_go.png" title="clock_go.png" alt="clock go "/><em>clock_go.png</em></td>

+		<td><img src="icons/clock_link.png" title="clock_link.png" alt="clock link "/><em>clock_link.png</em></td>

+

+		<td><img src="icons/clock_pause.png" title="clock_pause.png" alt="clock pause "/><em>clock_pause.png</em></td>

+		<td><img src="icons/clock_play.png" title="clock_play.png" alt="clock play "/><em>clock_play.png</em></td>

+		<td><img src="icons/clock_red.png" title="clock_red.png" alt="clock red "/><em>clock_red.png</em></td>

+		<td><img src="icons/clock_stop.png" title="clock_stop.png" alt="clock stop "/><em>clock_stop.png</em></td>

+		<td><img src="icons/cog.png" title="cog.png" alt="cog "/><em>cog.png</em></td>

+		<td><img src="icons/cog_add.png" title="cog_add.png" alt="cog add "/><em>cog_add.png</em></td>

+

+		<td><img src="icons/cog_delete.png" title="cog_delete.png" alt="cog delete "/><em>cog_delete.png</em></td>

+		<td><img src="icons/cog_edit.png" title="cog_edit.png" alt="cog edit "/><em>cog_edit.png</em></td>

+		<td><img src="icons/cog_error.png" title="cog_error.png" alt="cog error "/><em>cog_error.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/cog_go.png" title="cog_go.png" alt="cog go "/><em>cog_go.png</em></td>

+		<td><img src="icons/coins.png" title="coins.png" alt="coins "/><em>coins.png</em></td>

+

+		<td><img src="icons/coins_add.png" title="coins_add.png" alt="coins add "/><em>coins_add.png</em></td>

+		<td><img src="icons/coins_delete.png" title="coins_delete.png" alt="coins delete "/><em>coins_delete.png</em></td>

+		<td><img src="icons/color_swatch.png" title="color_swatch.png" alt="color swatch "/><em>color_swatch.png</em></td>

+		<td><img src="icons/color_wheel.png" title="color_wheel.png" alt="color wheel "/><em>color_wheel.png</em></td>

+		<td><img src="icons/comment.png" title="comment.png" alt="comment "/><em>comment.png</em></td>

+		<td><img src="icons/comment_add.png" title="comment_add.png" alt="comment add "/><em>comment_add.png</em></td>

+

+		<td><img src="icons/comment_delete.png" title="comment_delete.png" alt="comment delete "/><em>comment_delete.png</em></td>

+		<td><img src="icons/comment_edit.png" title="comment_edit.png" alt="comment edit "/><em>comment_edit.png</em></td>

+		<td><img src="icons/comments.png" title="comments.png" alt="comments "/><em>comments.png</em></td>

+		<td><img src="icons/comments_add.png" title="comments_add.png" alt="comments add "/><em>comments_add.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/comments_delete.png" title="comments_delete.png" alt="comments delete "/><em>comments_delete.png</em></td>

+

+		<td><img src="icons/compress.png" title="compress.png" alt="compress "/><em>compress.png</em></td>

+		<td><img src="icons/computer.png" title="computer.png" alt="computer "/><em>computer.png</em></td>

+		<td><img src="icons/computer_add.png" title="computer_add.png" alt="computer add "/><em>computer_add.png</em></td>

+		<td><img src="icons/computer_delete.png" title="computer_delete.png" alt="computer delete "/><em>computer_delete.png</em></td>

+		<td><img src="icons/computer_edit.png" title="computer_edit.png" alt="computer edit "/><em>computer_edit.png</em></td>

+		<td><img src="icons/computer_error.png" title="computer_error.png" alt="computer error "/><em>computer_error.png</em></td>

+

+		<td><img src="icons/computer_go.png" title="computer_go.png" alt="computer go "/><em>computer_go.png</em></td>

+		<td><img src="icons/computer_key.png" title="computer_key.png" alt="computer key "/><em>computer_key.png</em></td>

+		<td><img src="icons/computer_link.png" title="computer_link.png" alt="computer link "/><em>computer_link.png</em></td>

+		<td><img src="icons/connect.png" title="connect.png" alt="connect "/><em>connect.png</em></td>

+		<td><img src="icons/contrast.png" title="contrast.png" alt="contrast "/><em>contrast.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/contrast_decrease.png" title="contrast_decrease.png" alt="contrast decrease "/><em>contrast_decrease.png</em></td>

+		<td><img src="icons/contrast_high.png" title="contrast_high.png" alt="contrast high "/><em>contrast_high.png</em></td>

+		<td><img src="icons/contrast_increase.png" title="contrast_increase.png" alt="contrast increase "/><em>contrast_increase.png</em></td>

+		<td><img src="icons/contrast_low.png" title="contrast_low.png" alt="contrast low "/><em>contrast_low.png</em></td>

+		<td><img src="icons/control_eject.png" title="control_eject.png" alt="control eject "/><em>control_eject.png</em></td>

+

+		<td><img src="icons/control_eject_blue.png" title="control_eject_blue.png" alt="control eject blue "/><em>control_eject_blue.png</em></td>

+		<td><img src="icons/control_end.png" title="control_end.png" alt="control end "/><em>control_end.png</em></td>

+		<td><img src="icons/control_end_blue.png" title="control_end_blue.png" alt="control end blue "/><em>control_end_blue.png</em></td>

+		<td><img src="icons/control_equalizer.png" title="control_equalizer.png" alt="control equalizer "/><em>control_equalizer.png</em></td>

+		<td><img src="icons/control_equalizer_blue.png" title="control_equalizer_blue.png" alt="control equalizer blue "/><em>control_equalizer_blue.png</em></td>

+		<td><img src="icons/control_fastforward.png" title="control_fastforward.png" alt="control fastforward "/><em>control_fastforward.png</em></td>

+

+		<td><img src="icons/control_fastforward_blue.png" title="control_fastforward_blue.png" alt="control fastforward blue "/><em>control_fastforward_blue.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/control_pause.png" title="control_pause.png" alt="control pause "/><em>control_pause.png</em></td>

+		<td><img src="icons/control_pause_blue.png" title="control_pause_blue.png" alt="control pause blue "/><em>control_pause_blue.png</em></td>

+		<td><img src="icons/control_play.png" title="control_play.png" alt="control play "/><em>control_play.png</em></td>

+		<td><img src="icons/control_play_blue.png" title="control_play_blue.png" alt="control play blue "/><em>control_play_blue.png</em></td>

+

+		<td><img src="icons/control_repeat.png" title="control_repeat.png" alt="control repeat "/><em>control_repeat.png</em></td>

+		<td><img src="icons/control_repeat_blue.png" title="control_repeat_blue.png" alt="control repeat blue "/><em>control_repeat_blue.png</em></td>

+		<td><img src="icons/control_rewind.png" title="control_rewind.png" alt="control rewind "/><em>control_rewind.png</em></td>

+		<td><img src="icons/control_rewind_blue.png" title="control_rewind_blue.png" alt="control rewind blue "/><em>control_rewind_blue.png</em></td>

+		<td><img src="icons/control_start.png" title="control_start.png" alt="control start "/><em>control_start.png</em></td>

+		<td><img src="icons/control_start_blue.png" title="control_start_blue.png" alt="control start blue "/><em>control_start_blue.png</em></td>

+

+		<td><img src="icons/control_stop.png" title="control_stop.png" alt="control stop "/><em>control_stop.png</em></td>

+		<td><img src="icons/control_stop_blue.png" title="control_stop_blue.png" alt="control stop blue "/><em>control_stop_blue.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/controller.png" title="controller.png" alt="controller "/><em>controller.png</em></td>

+		<td><img src="icons/controller_add.png" title="controller_add.png" alt="controller add "/><em>controller_add.png</em></td>

+		<td><img src="icons/controller_delete.png" title="controller_delete.png" alt="controller delete "/><em>controller_delete.png</em></td>

+

+		<td><img src="icons/controller_error.png" title="controller_error.png" alt="controller error "/><em>controller_error.png</em></td>

+		<td><img src="icons/creditcards.png" title="creditcards.png" alt="creditcards "/><em>creditcards.png</em></td>

+		<td><img src="icons/cross.png" title="cross.png" alt="cross "/><em>cross.png</em></td>

+		<td><img src="icons/css.png" title="css.png" alt="css "/><em>css.png</em></td>

+		<td><img src="icons/css_add.png" title="css_add.png" alt="css add "/><em>css_add.png</em></td>

+		<td><img src="icons/css_delete.png" title="css_delete.png" alt="css delete "/><em>css_delete.png</em></td>

+

+		<td><img src="icons/css_go.png" title="css_go.png" alt="css go "/><em>css_go.png</em></td>

+		<td><img src="icons/css_valid.png" title="css_valid.png" alt="css valid "/><em>css_valid.png</em></td>

+		<td><img src="icons/cup.png" title="cup.png" alt="cup "/><em>cup.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/cup_add.png" title="cup_add.png" alt="cup add "/><em>cup_add.png</em></td>

+		<td><img src="icons/cup_delete.png" title="cup_delete.png" alt="cup delete "/><em>cup_delete.png</em></td>

+

+		<td><img src="icons/cup_edit.png" title="cup_edit.png" alt="cup edit "/><em>cup_edit.png</em></td>

+		<td><img src="icons/cup_error.png" title="cup_error.png" alt="cup error "/><em>cup_error.png</em></td>

+		<td><img src="icons/cup_go.png" title="cup_go.png" alt="cup go "/><em>cup_go.png</em></td>

+		<td><img src="icons/cup_key.png" title="cup_key.png" alt="cup key "/><em>cup_key.png</em></td>

+		<td><img src="icons/cup_link.png" title="cup_link.png" alt="cup link "/><em>cup_link.png</em></td>

+		<td><img src="icons/cursor.png" title="cursor.png" alt="cursor "/><em>cursor.png</em></td>

+

+		<td><img src="icons/cut.png" title="cut.png" alt="cut "/><em>cut.png</em></td>

+		<td><img src="icons/cut_red.png" title="cut_red.png" alt="cut red "/><em>cut_red.png</em></td>

+		<td><img src="icons/database.png" title="database.png" alt="database "/><em>database.png</em></td>

+		<td><img src="icons/database_add.png" title="database_add.png" alt="database add "/><em>database_add.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/database_connect.png" title="database_connect.png" alt="database connect "/><em>database_connect.png</em></td>

+

+		<td><img src="icons/database_delete.png" title="database_delete.png" alt="database delete "/><em>database_delete.png</em></td>

+		<td><img src="icons/database_edit.png" title="database_edit.png" alt="database edit "/><em>database_edit.png</em></td>

+		<td><img src="icons/database_error.png" title="database_error.png" alt="database error "/><em>database_error.png</em></td>

+		<td><img src="icons/database_gear.png" title="database_gear.png" alt="database gear "/><em>database_gear.png</em></td>

+		<td><img src="icons/database_go.png" title="database_go.png" alt="database go "/><em>database_go.png</em></td>

+		<td><img src="icons/database_key.png" title="database_key.png" alt="database key "/><em>database_key.png</em></td>

+

+		<td><img src="icons/database_lightning.png" title="database_lightning.png" alt="database lightning "/><em>database_lightning.png</em></td>

+		<td><img src="icons/database_link.png" title="database_link.png" alt="database link "/><em>database_link.png</em></td>

+		<td><img src="icons/database_refresh.png" title="database_refresh.png" alt="database refresh "/><em>database_refresh.png</em></td>

+		<td><img src="icons/database_save.png" title="database_save.png" alt="database save "/><em>database_save.png</em></td>

+		<td><img src="icons/database_table.png" title="database_table.png" alt="database table "/><em>database_table.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/date.png" title="date.png" alt="date "/><em>date.png</em></td>

+		<td><img src="icons/date_add.png" title="date_add.png" alt="date add "/><em>date_add.png</em></td>

+		<td><img src="icons/date_delete.png" title="date_delete.png" alt="date delete "/><em>date_delete.png</em></td>

+		<td><img src="icons/date_edit.png" title="date_edit.png" alt="date edit "/><em>date_edit.png</em></td>

+		<td><img src="icons/date_error.png" title="date_error.png" alt="date error "/><em>date_error.png</em></td>

+

+		<td><img src="icons/date_go.png" title="date_go.png" alt="date go "/><em>date_go.png</em></td>

+		<td><img src="icons/date_link.png" title="date_link.png" alt="date link "/><em>date_link.png</em></td>

+		<td><img src="icons/date_magnify.png" title="date_magnify.png" alt="date magnify "/><em>date_magnify.png</em></td>

+		<td><img src="icons/date_next.png" title="date_next.png" alt="date next "/><em>date_next.png</em></td>

+		<td><img src="icons/date_previous.png" title="date_previous.png" alt="date previous "/><em>date_previous.png</em></td>

+		<td><img src="icons/delete.png" title="delete.png" alt="delete "/><em>delete.png</em></td>

+

+		<td><img src="icons/disconnect.png" title="disconnect.png" alt="disconnect "/><em>disconnect.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/disk.png" title="disk.png" alt="disk "/><em>disk.png</em></td>

+		<td><img src="icons/disk_multiple.png" title="disk_multiple.png" alt="disk multiple "/><em>disk_multiple.png</em></td>

+		<td><img src="icons/door.png" title="door.png" alt="door "/><em>door.png</em></td>

+		<td><img src="icons/door_in.png" title="door_in.png" alt="door in "/><em>door_in.png</em></td>

+

+		<td><img src="icons/door_open.png" title="door_open.png" alt="door open "/><em>door_open.png</em></td>

+		<td><img src="icons/door_out.png" title="door_out.png" alt="door out "/><em>door_out.png</em></td>

+		<td><img src="icons/drink.png" title="drink.png" alt="drink "/><em>drink.png</em></td>

+		<td><img src="icons/drink_empty.png" title="drink_empty.png" alt="drink empty "/><em>drink_empty.png</em></td>

+		<td><img src="icons/drive.png" title="drive.png" alt="drive "/><em>drive.png</em></td>

+		<td><img src="icons/drive_add.png" title="drive_add.png" alt="drive add "/><em>drive_add.png</em></td>

+

+		<td><img src="icons/drive_burn.png" title="drive_burn.png" alt="drive burn "/><em>drive_burn.png</em></td>

+		<td><img src="icons/drive_cd.png" title="drive_cd.png" alt="drive cd "/><em>drive_cd.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/drive_cd_empty.png" title="drive_cd_empty.png" alt="drive cd empty "/><em>drive_cd_empty.png</em></td>

+		<td><img src="icons/drive_delete.png" title="drive_delete.png" alt="drive delete "/><em>drive_delete.png</em></td>

+		<td><img src="icons/drive_disk.png" title="drive_disk.png" alt="drive disk "/><em>drive_disk.png</em></td>

+

+		<td><img src="icons/drive_edit.png" title="drive_edit.png" alt="drive edit "/><em>drive_edit.png</em></td>

+		<td><img src="icons/drive_error.png" title="drive_error.png" alt="drive error "/><em>drive_error.png</em></td>

+		<td><img src="icons/drive_go.png" title="drive_go.png" alt="drive go "/><em>drive_go.png</em></td>

+		<td><img src="icons/drive_key.png" title="drive_key.png" alt="drive key "/><em>drive_key.png</em></td>

+		<td><img src="icons/drive_link.png" title="drive_link.png" alt="drive link "/><em>drive_link.png</em></td>

+		<td><img src="icons/drive_magnify.png" title="drive_magnify.png" alt="drive magnify "/><em>drive_magnify.png</em></td>

+

+		<td><img src="icons/drive_network.png" title="drive_network.png" alt="drive network "/><em>drive_network.png</em></td>

+		<td><img src="icons/drive_rename.png" title="drive_rename.png" alt="drive rename "/><em>drive_rename.png</em></td>

+		<td><img src="icons/drive_user.png" title="drive_user.png" alt="drive user "/><em>drive_user.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/drive_web.png" title="drive_web.png" alt="drive web "/><em>drive_web.png</em></td>

+		<td><img src="icons/dvd.png" title="dvd.png" alt="dvd "/><em>dvd.png</em></td>

+

+		<td><img src="icons/dvd_add.png" title="dvd_add.png" alt="dvd add "/><em>dvd_add.png</em></td>

+		<td><img src="icons/dvd_delete.png" title="dvd_delete.png" alt="dvd delete "/><em>dvd_delete.png</em></td>

+		<td><img src="icons/dvd_edit.png" title="dvd_edit.png" alt="dvd edit "/><em>dvd_edit.png</em></td>

+		<td><img src="icons/dvd_error.png" title="dvd_error.png" alt="dvd error "/><em>dvd_error.png</em></td>

+		<td><img src="icons/dvd_go.png" title="dvd_go.png" alt="dvd go "/><em>dvd_go.png</em></td>

+		<td><img src="icons/dvd_key.png" title="dvd_key.png" alt="dvd key "/><em>dvd_key.png</em></td>

+

+		<td><img src="icons/dvd_link.png" title="dvd_link.png" alt="dvd link "/><em>dvd_link.png</em></td>

+		<td><img src="icons/email.png" title="email.png" alt="email "/><em>email.png</em></td>

+		<td><img src="icons/email_add.png" title="email_add.png" alt="email add "/><em>email_add.png</em></td>

+		<td><img src="icons/email_attach.png" title="email_attach.png" alt="email attach "/><em>email_attach.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/email_delete.png" title="email_delete.png" alt="email delete "/><em>email_delete.png</em></td>

+

+		<td><img src="icons/email_edit.png" title="email_edit.png" alt="email edit "/><em>email_edit.png</em></td>

+		<td><img src="icons/email_error.png" title="email_error.png" alt="email error "/><em>email_error.png</em></td>

+		<td><img src="icons/email_go.png" title="email_go.png" alt="email go "/><em>email_go.png</em></td>

+		<td><img src="icons/email_link.png" title="email_link.png" alt="email link "/><em>email_link.png</em></td>

+		<td><img src="icons/email_open.png" title="email_open.png" alt="email open "/><em>email_open.png</em></td>

+		<td><img src="icons/email_open_image.png" title="email_open_image.png" alt="email open image "/><em>email_open_image.png</em></td>

+

+		<td><img src="icons/emoticon_evilgrin.png" title="emoticon_evilgrin.png" alt="emoticon evilgrin "/><em>emoticon_evilgrin.png</em></td>

+		<td><img src="icons/emoticon_grin.png" title="emoticon_grin.png" alt="emoticon grin "/><em>emoticon_grin.png</em></td>

+		<td><img src="icons/emoticon_happy.png" title="emoticon_happy.png" alt="emoticon happy "/><em>emoticon_happy.png</em></td>

+		<td><img src="icons/emoticon_smile.png" title="emoticon_smile.png" alt="emoticon smile "/><em>emoticon_smile.png</em></td>

+		<td><img src="icons/emoticon_surprised.png" title="emoticon_surprised.png" alt="emoticon surprised "/><em>emoticon_surprised.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/emoticon_tongue.png" title="emoticon_tongue.png" alt="emoticon tongue "/><em>emoticon_tongue.png</em></td>

+		<td><img src="icons/emoticon_unhappy.png" title="emoticon_unhappy.png" alt="emoticon unhappy "/><em>emoticon_unhappy.png</em></td>

+		<td><img src="icons/emoticon_waii.png" title="emoticon_waii.png" alt="emoticon waii "/><em>emoticon_waii.png</em></td>

+		<td><img src="icons/emoticon_wink.png" title="emoticon_wink.png" alt="emoticon wink "/><em>emoticon_wink.png</em></td>

+		<td><img src="icons/error.png" title="error.png" alt="error "/><em>error.png</em></td>

+

+		<td><img src="icons/error_add.png" title="error_add.png" alt="error add "/><em>error_add.png</em></td>

+		<td><img src="icons/error_delete.png" title="error_delete.png" alt="error delete "/><em>error_delete.png</em></td>

+		<td><img src="icons/error_go.png" title="error_go.png" alt="error go "/><em>error_go.png</em></td>

+		<td><img src="icons/exclamation.png" title="exclamation.png" alt="exclamation "/><em>exclamation.png</em></td>

+		<td><img src="icons/eye.png" title="eye.png" alt="eye "/><em>eye.png</em></td>

+		<td><img src="icons/feed.png" title="feed.png" alt="feed "/><em>feed.png</em></td>

+

+		<td><img src="icons/feed_add.png" title="feed_add.png" alt="feed add "/><em>feed_add.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/feed_delete.png" title="feed_delete.png" alt="feed delete "/><em>feed_delete.png</em></td>

+		<td><img src="icons/feed_disk.png" title="feed_disk.png" alt="feed disk "/><em>feed_disk.png</em></td>

+		<td><img src="icons/feed_edit.png" title="feed_edit.png" alt="feed edit "/><em>feed_edit.png</em></td>

+		<td><img src="icons/feed_error.png" title="feed_error.png" alt="feed error "/><em>feed_error.png</em></td>

+

+		<td><img src="icons/feed_go.png" title="feed_go.png" alt="feed go "/><em>feed_go.png</em></td>

+		<td><img src="icons/feed_key.png" title="feed_key.png" alt="feed key "/><em>feed_key.png</em></td>

+		<td><img src="icons/feed_link.png" title="feed_link.png" alt="feed link "/><em>feed_link.png</em></td>

+		<td><img src="icons/feed_magnify.png" title="feed_magnify.png" alt="feed magnify "/><em>feed_magnify.png</em></td>

+		<td><img src="icons/female.png" title="female.png" alt="female "/><em>female.png</em></td>

+		<td><img src="icons/film.png" title="film.png" alt="film "/><em>film.png</em></td>

+

+		<td><img src="icons/film_add.png" title="film_add.png" alt="film add "/><em>film_add.png</em></td>

+		<td><img src="icons/film_delete.png" title="film_delete.png" alt="film delete "/><em>film_delete.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/film_edit.png" title="film_edit.png" alt="film edit "/><em>film_edit.png</em></td>

+		<td><img src="icons/film_error.png" title="film_error.png" alt="film error "/><em>film_error.png</em></td>

+		<td><img src="icons/film_go.png" title="film_go.png" alt="film go "/><em>film_go.png</em></td>

+

+		<td><img src="icons/film_key.png" title="film_key.png" alt="film key "/><em>film_key.png</em></td>

+		<td><img src="icons/film_link.png" title="film_link.png" alt="film link "/><em>film_link.png</em></td>

+		<td><img src="icons/film_save.png" title="film_save.png" alt="film save "/><em>film_save.png</em></td>

+		<td><img src="icons/find.png" title="find.png" alt="find "/><em>find.png</em></td>

+		<td><img src="icons/flag_blue.png" title="flag_blue.png" alt="flag blue "/><em>flag_blue.png</em></td>

+		<td><img src="icons/flag_green.png" title="flag_green.png" alt="flag green "/><em>flag_green.png</em></td>

+

+		<td><img src="icons/flag_orange.png" title="flag_orange.png" alt="flag orange "/><em>flag_orange.png</em></td>

+		<td><img src="icons/flag_pink.png" title="flag_pink.png" alt="flag pink "/><em>flag_pink.png</em></td>

+		<td><img src="icons/flag_purple.png" title="flag_purple.png" alt="flag purple "/><em>flag_purple.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/flag_red.png" title="flag_red.png" alt="flag red "/><em>flag_red.png</em></td>

+		<td><img src="icons/flag_yellow.png" title="flag_yellow.png" alt="flag yellow "/><em>flag_yellow.png</em></td>

+

+		<td><img src="icons/folder.png" title="folder.png" alt="folder "/><em>folder.png</em></td>

+		<td><img src="icons/folder_add.png" title="folder_add.png" alt="folder add "/><em>folder_add.png</em></td>

+		<td><img src="icons/folder_bell.png" title="folder_bell.png" alt="folder bell "/><em>folder_bell.png</em></td>

+		<td><img src="icons/folder_brick.png" title="folder_brick.png" alt="folder brick "/><em>folder_brick.png</em></td>

+		<td><img src="icons/folder_bug.png" title="folder_bug.png" alt="folder bug "/><em>folder_bug.png</em></td>

+		<td><img src="icons/folder_camera.png" title="folder_camera.png" alt="folder camera "/><em>folder_camera.png</em></td>

+

+		<td><img src="icons/folder_database.png" title="folder_database.png" alt="folder database "/><em>folder_database.png</em></td>

+		<td><img src="icons/folder_delete.png" title="folder_delete.png" alt="folder delete "/><em>folder_delete.png</em></td>

+		<td><img src="icons/folder_edit.png" title="folder_edit.png" alt="folder edit "/><em>folder_edit.png</em></td>

+		<td><img src="icons/folder_error.png" title="folder_error.png" alt="folder error "/><em>folder_error.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/folder_explore.png" title="folder_explore.png" alt="folder explore "/><em>folder_explore.png</em></td>

+

+		<td><img src="icons/folder_feed.png" title="folder_feed.png" alt="folder feed "/><em>folder_feed.png</em></td>

+		<td><img src="icons/folder_find.png" title="folder_find.png" alt="folder find "/><em>folder_find.png</em></td>

+		<td><img src="icons/folder_go.png" title="folder_go.png" alt="folder go "/><em>folder_go.png</em></td>

+		<td><img src="icons/folder_heart.png" title="folder_heart.png" alt="folder heart "/><em>folder_heart.png</em></td>

+		<td><img src="icons/folder_image.png" title="folder_image.png" alt="folder image "/><em>folder_image.png</em></td>

+		<td><img src="icons/folder_key.png" title="folder_key.png" alt="folder key "/><em>folder_key.png</em></td>

+

+		<td><img src="icons/folder_lightbulb.png" title="folder_lightbulb.png" alt="folder lightbulb "/><em>folder_lightbulb.png</em></td>

+		<td><img src="icons/folder_link.png" title="folder_link.png" alt="folder link "/><em>folder_link.png</em></td>

+		<td><img src="icons/folder_magnify.png" title="folder_magnify.png" alt="folder magnify "/><em>folder_magnify.png</em></td>

+		<td><img src="icons/folder_page.png" title="folder_page.png" alt="folder page "/><em>folder_page.png</em></td>

+		<td><img src="icons/folder_page_white.png" title="folder_page_white.png" alt="folder page white "/><em>folder_page_white.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/folder_palette.png" title="folder_palette.png" alt="folder palette "/><em>folder_palette.png</em></td>

+		<td><img src="icons/folder_picture.png" title="folder_picture.png" alt="folder picture "/><em>folder_picture.png</em></td>

+		<td><img src="icons/folder_star.png" title="folder_star.png" alt="folder star "/><em>folder_star.png</em></td>

+		<td><img src="icons/folder_table.png" title="folder_table.png" alt="folder table "/><em>folder_table.png</em></td>

+		<td><img src="icons/folder_user.png" title="folder_user.png" alt="folder user "/><em>folder_user.png</em></td>

+

+		<td><img src="icons/folder_wrench.png" title="folder_wrench.png" alt="folder wrench "/><em>folder_wrench.png</em></td>

+		<td><img src="icons/font.png" title="font.png" alt="font "/><em>font.png</em></td>

+		<td><img src="icons/font_add.png" title="font_add.png" alt="font add "/><em>font_add.png</em></td>

+		<td><img src="icons/font_delete.png" title="font_delete.png" alt="font delete "/><em>font_delete.png</em></td>

+		<td><img src="icons/font_go.png" title="font_go.png" alt="font go "/><em>font_go.png</em></td>

+		<td><img src="icons/group.png" title="group.png" alt="group "/><em>group.png</em></td>

+

+		<td><img src="icons/group_add.png" title="group_add.png" alt="group add "/><em>group_add.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/group_delete.png" title="group_delete.png" alt="group delete "/><em>group_delete.png</em></td>

+		<td><img src="icons/group_edit.png" title="group_edit.png" alt="group edit "/><em>group_edit.png</em></td>

+		<td><img src="icons/group_error.png" title="group_error.png" alt="group error "/><em>group_error.png</em></td>

+		<td><img src="icons/group_gear.png" title="group_gear.png" alt="group gear "/><em>group_gear.png</em></td>

+

+		<td><img src="icons/group_go.png" title="group_go.png" alt="group go "/><em>group_go.png</em></td>

+		<td><img src="icons/group_key.png" title="group_key.png" alt="group key "/><em>group_key.png</em></td>

+		<td><img src="icons/group_link.png" title="group_link.png" alt="group link "/><em>group_link.png</em></td>

+		<td><img src="icons/heart.png" title="heart.png" alt="heart "/><em>heart.png</em></td>

+		<td><img src="icons/heart_add.png" title="heart_add.png" alt="heart add "/><em>heart_add.png</em></td>

+		<td><img src="icons/heart_delete.png" title="heart_delete.png" alt="heart delete "/><em>heart_delete.png</em></td>

+

+		<td><img src="icons/help.png" title="help.png" alt="help "/><em>help.png</em></td>

+		<td><img src="icons/hourglass.png" title="hourglass.png" alt="hourglass "/><em>hourglass.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/hourglass_add.png" title="hourglass_add.png" alt="hourglass add "/><em>hourglass_add.png</em></td>

+		<td><img src="icons/hourglass_delete.png" title="hourglass_delete.png" alt="hourglass delete "/><em>hourglass_delete.png</em></td>

+		<td><img src="icons/hourglass_go.png" title="hourglass_go.png" alt="hourglass go "/><em>hourglass_go.png</em></td>

+

+		<td><img src="icons/hourglass_link.png" title="hourglass_link.png" alt="hourglass link "/><em>hourglass_link.png</em></td>

+		<td><img src="icons/house.png" title="house.png" alt="house "/><em>house.png</em></td>

+		<td><img src="icons/house_go.png" title="house_go.png" alt="house go "/><em>house_go.png</em></td>

+		<td><img src="icons/house_link.png" title="house_link.png" alt="house link "/><em>house_link.png</em></td>

+		<td><img src="icons/html.png" title="html.png" alt="html "/><em>html.png</em></td>

+		<td><img src="icons/html_add.png" title="html_add.png" alt="html add "/><em>html_add.png</em></td>

+

+		<td><img src="icons/html_delete.png" title="html_delete.png" alt="html delete "/><em>html_delete.png</em></td>

+		<td><img src="icons/html_go.png" title="html_go.png" alt="html go "/><em>html_go.png</em></td>

+		<td><img src="icons/html_valid.png" title="html_valid.png" alt="html valid "/><em>html_valid.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/image.png" title="image.png" alt="image "/><em>image.png</em></td>

+		<td><img src="icons/image_add.png" title="image_add.png" alt="image add "/><em>image_add.png</em></td>

+

+		<td><img src="icons/image_delete.png" title="image_delete.png" alt="image delete "/><em>image_delete.png</em></td>

+		<td><img src="icons/image_edit.png" title="image_edit.png" alt="image edit "/><em>image_edit.png</em></td>

+		<td><img src="icons/image_link.png" title="image_link.png" alt="image link "/><em>image_link.png</em></td>

+		<td><img src="icons/images.png" title="images.png" alt="images "/><em>images.png</em></td>

+		<td><img src="icons/information.png" title="information.png" alt="information "/><em>information.png</em></td>

+		<td><img src="icons/ipod.png" title="ipod.png" alt="ipod "/><em>ipod.png</em></td>

+

+		<td><img src="icons/ipod_cast.png" title="ipod_cast.png" alt="ipod cast "/><em>ipod_cast.png</em></td>

+		<td><img src="icons/ipod_cast_add.png" title="ipod_cast_add.png" alt="ipod cast add "/><em>ipod_cast_add.png</em></td>

+		<td><img src="icons/ipod_cast_delete.png" title="ipod_cast_delete.png" alt="ipod cast delete "/><em>ipod_cast_delete.png</em></td>

+		<td><img src="icons/ipod_sound.png" title="ipod_sound.png" alt="ipod sound "/><em>ipod_sound.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/joystick.png" title="joystick.png" alt="joystick "/><em>joystick.png</em></td>

+

+		<td><img src="icons/joystick_add.png" title="joystick_add.png" alt="joystick add "/><em>joystick_add.png</em></td>

+		<td><img src="icons/joystick_delete.png" title="joystick_delete.png" alt="joystick delete "/><em>joystick_delete.png</em></td>

+		<td><img src="icons/joystick_error.png" title="joystick_error.png" alt="joystick error "/><em>joystick_error.png</em></td>

+		<td><img src="icons/key.png" title="key.png" alt="key "/><em>key.png</em></td>

+		<td><img src="icons/key_add.png" title="key_add.png" alt="key add "/><em>key_add.png</em></td>

+		<td><img src="icons/key_delete.png" title="key_delete.png" alt="key delete "/><em>key_delete.png</em></td>

+

+		<td><img src="icons/key_go.png" title="key_go.png" alt="key go "/><em>key_go.png</em></td>

+		<td><img src="icons/keyboard.png" title="keyboard.png" alt="keyboard "/><em>keyboard.png</em></td>

+		<td><img src="icons/keyboard_add.png" title="keyboard_add.png" alt="keyboard add "/><em>keyboard_add.png</em></td>

+		<td><img src="icons/keyboard_delete.png" title="keyboard_delete.png" alt="keyboard delete "/><em>keyboard_delete.png</em></td>

+		<td><img src="icons/keyboard_magnify.png" title="keyboard_magnify.png" alt="keyboard magnify "/><em>keyboard_magnify.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/layers.png" title="layers.png" alt="layers "/><em>layers.png</em></td>

+		<td><img src="icons/layout.png" title="layout.png" alt="layout "/><em>layout.png</em></td>

+		<td><img src="icons/layout_add.png" title="layout_add.png" alt="layout add "/><em>layout_add.png</em></td>

+		<td><img src="icons/layout_content.png" title="layout_content.png" alt="layout content "/><em>layout_content.png</em></td>

+		<td><img src="icons/layout_delete.png" title="layout_delete.png" alt="layout delete "/><em>layout_delete.png</em></td>

+

+		<td><img src="icons/layout_edit.png" title="layout_edit.png" alt="layout edit "/><em>layout_edit.png</em></td>

+		<td><img src="icons/layout_error.png" title="layout_error.png" alt="layout error "/><em>layout_error.png</em></td>

+		<td><img src="icons/layout_header.png" title="layout_header.png" alt="layout header "/><em>layout_header.png</em></td>

+		<td><img src="icons/layout_link.png" title="layout_link.png" alt="layout link "/><em>layout_link.png</em></td>

+		<td><img src="icons/layout_sidebar.png" title="layout_sidebar.png" alt="layout sidebar "/><em>layout_sidebar.png</em></td>

+		<td><img src="icons/lightbulb.png" title="lightbulb.png" alt="lightbulb "/><em>lightbulb.png</em></td>

+

+		<td><img src="icons/lightbulb_add.png" title="lightbulb_add.png" alt="lightbulb add "/><em>lightbulb_add.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/lightbulb_delete.png" title="lightbulb_delete.png" alt="lightbulb delete "/><em>lightbulb_delete.png</em></td>

+		<td><img src="icons/lightbulb_off.png" title="lightbulb_off.png" alt="lightbulb off "/><em>lightbulb_off.png</em></td>

+		<td><img src="icons/lightning.png" title="lightning.png" alt="lightning "/><em>lightning.png</em></td>

+		<td><img src="icons/lightning_add.png" title="lightning_add.png" alt="lightning add "/><em>lightning_add.png</em></td>

+

+		<td><img src="icons/lightning_delete.png" title="lightning_delete.png" alt="lightning delete "/><em>lightning_delete.png</em></td>

+		<td><img src="icons/lightning_go.png" title="lightning_go.png" alt="lightning go "/><em>lightning_go.png</em></td>

+		<td><img src="icons/link.png" title="link.png" alt="link "/><em>link.png</em></td>

+		<td><img src="icons/link_add.png" title="link_add.png" alt="link add "/><em>link_add.png</em></td>

+		<td><img src="icons/link_break.png" title="link_break.png" alt="link break "/><em>link_break.png</em></td>

+		<td><img src="icons/link_delete.png" title="link_delete.png" alt="link delete "/><em>link_delete.png</em></td>

+

+		<td><img src="icons/link_edit.png" title="link_edit.png" alt="link edit "/><em>link_edit.png</em></td>

+		<td><img src="icons/link_error.png" title="link_error.png" alt="link error "/><em>link_error.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/link_go.png" title="link_go.png" alt="link go "/><em>link_go.png</em></td>

+		<td><img src="icons/lock.png" title="lock.png" alt="lock "/><em>lock.png</em></td>

+		<td><img src="icons/lock_add.png" title="lock_add.png" alt="lock add "/><em>lock_add.png</em></td>

+

+		<td><img src="icons/lock_break.png" title="lock_break.png" alt="lock break "/><em>lock_break.png</em></td>

+		<td><img src="icons/lock_delete.png" title="lock_delete.png" alt="lock delete "/><em>lock_delete.png</em></td>

+		<td><img src="icons/lock_edit.png" title="lock_edit.png" alt="lock edit "/><em>lock_edit.png</em></td>

+		<td><img src="icons/lock_go.png" title="lock_go.png" alt="lock go "/><em>lock_go.png</em></td>

+		<td><img src="icons/lock_open.png" title="lock_open.png" alt="lock open "/><em>lock_open.png</em></td>

+		<td><img src="icons/lorry.png" title="lorry.png" alt="lorry "/><em>lorry.png</em></td>

+

+		<td><img src="icons/lorry_add.png" title="lorry_add.png" alt="lorry add "/><em>lorry_add.png</em></td>

+		<td><img src="icons/lorry_delete.png" title="lorry_delete.png" alt="lorry delete "/><em>lorry_delete.png</em></td>

+		<td><img src="icons/lorry_error.png" title="lorry_error.png" alt="lorry error "/><em>lorry_error.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/lorry_flatbed.png" title="lorry_flatbed.png" alt="lorry flatbed "/><em>lorry_flatbed.png</em></td>

+		<td><img src="icons/lorry_go.png" title="lorry_go.png" alt="lorry go "/><em>lorry_go.png</em></td>

+

+		<td><img src="icons/lorry_link.png" title="lorry_link.png" alt="lorry link "/><em>lorry_link.png</em></td>

+		<td><img src="icons/magifier_zoom_out.png" title="magifier_zoom_out.png" alt="magifier zoom out "/><em>magifier_zoom_out.png</em></td>

+		<td><img src="icons/magnifier.png" title="magnifier.png" alt="magnifier "/><em>magnifier.png</em></td>

+		<td><img src="icons/magnifier_zoom_in.png" title="magnifier_zoom_in.png" alt="magnifier zoom in "/><em>magnifier_zoom_in.png</em></td>

+		<td><img src="icons/male.png" title="male.png" alt="male "/><em>male.png</em></td>

+		<td><img src="icons/map.png" title="map.png" alt="map "/><em>map.png</em></td>

+

+		<td><img src="icons/map_add.png" title="map_add.png" alt="map add "/><em>map_add.png</em></td>

+		<td><img src="icons/map_delete.png" title="map_delete.png" alt="map delete "/><em>map_delete.png</em></td>

+		<td><img src="icons/map_edit.png" title="map_edit.png" alt="map edit "/><em>map_edit.png</em></td>

+		<td><img src="icons/map_go.png" title="map_go.png" alt="map go "/><em>map_go.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/map_magnify.png" title="map_magnify.png" alt="map magnify "/><em>map_magnify.png</em></td>

+

+		<td><img src="icons/medal_bronze_1.png" title="medal_bronze_1.png" alt="medal bronze 1 "/><em>medal_bronze_1.png</em></td>

+		<td><img src="icons/medal_bronze_2.png" title="medal_bronze_2.png" alt="medal bronze 2 "/><em>medal_bronze_2.png</em></td>

+		<td><img src="icons/medal_bronze_3.png" title="medal_bronze_3.png" alt="medal bronze 3 "/><em>medal_bronze_3.png</em></td>

+		<td><img src="icons/medal_bronze_add.png" title="medal_bronze_add.png" alt="medal bronze add "/><em>medal_bronze_add.png</em></td>

+		<td><img src="icons/medal_bronze_delete.png" title="medal_bronze_delete.png" alt="medal bronze delete "/><em>medal_bronze_delete.png</em></td>

+		<td><img src="icons/medal_gold_1.png" title="medal_gold_1.png" alt="medal gold 1 "/><em>medal_gold_1.png</em></td>

+

+		<td><img src="icons/medal_gold_2.png" title="medal_gold_2.png" alt="medal gold 2 "/><em>medal_gold_2.png</em></td>

+		<td><img src="icons/medal_gold_3.png" title="medal_gold_3.png" alt="medal gold 3 "/><em>medal_gold_3.png</em></td>

+		<td><img src="icons/medal_gold_add.png" title="medal_gold_add.png" alt="medal gold add "/><em>medal_gold_add.png</em></td>

+		<td><img src="icons/medal_gold_delete.png" title="medal_gold_delete.png" alt="medal gold delete "/><em>medal_gold_delete.png</em></td>

+		<td><img src="icons/medal_silver_1.png" title="medal_silver_1.png" alt="medal silver 1 "/><em>medal_silver_1.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/medal_silver_2.png" title="medal_silver_2.png" alt="medal silver 2 "/><em>medal_silver_2.png</em></td>

+		<td><img src="icons/medal_silver_3.png" title="medal_silver_3.png" alt="medal silver 3 "/><em>medal_silver_3.png</em></td>

+		<td><img src="icons/medal_silver_add.png" title="medal_silver_add.png" alt="medal silver add "/><em>medal_silver_add.png</em></td>

+		<td><img src="icons/medal_silver_delete.png" title="medal_silver_delete.png" alt="medal silver delete "/><em>medal_silver_delete.png</em></td>

+		<td><img src="icons/money.png" title="money.png" alt="money "/><em>money.png</em></td>

+

+		<td><img src="icons/money_add.png" title="money_add.png" alt="money add "/><em>money_add.png</em></td>

+		<td><img src="icons/money_delete.png" title="money_delete.png" alt="money delete "/><em>money_delete.png</em></td>

+		<td><img src="icons/money_dollar.png" title="money_dollar.png" alt="money dollar "/><em>money_dollar.png</em></td>

+		<td><img src="icons/money_euro.png" title="money_euro.png" alt="money euro "/><em>money_euro.png</em></td>

+		<td><img src="icons/money_pound.png" title="money_pound.png" alt="money pound "/><em>money_pound.png</em></td>

+		<td><img src="icons/money_yen.png" title="money_yen.png" alt="money yen "/><em>money_yen.png</em></td>

+

+		<td><img src="icons/monitor.png" title="monitor.png" alt="monitor "/><em>monitor.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/monitor_add.png" title="monitor_add.png" alt="monitor add "/><em>monitor_add.png</em></td>

+		<td><img src="icons/monitor_delete.png" title="monitor_delete.png" alt="monitor delete "/><em>monitor_delete.png</em></td>

+		<td><img src="icons/monitor_edit.png" title="monitor_edit.png" alt="monitor edit "/><em>monitor_edit.png</em></td>

+		<td><img src="icons/monitor_error.png" title="monitor_error.png" alt="monitor error "/><em>monitor_error.png</em></td>

+

+		<td><img src="icons/monitor_go.png" title="monitor_go.png" alt="monitor go "/><em>monitor_go.png</em></td>

+		<td><img src="icons/monitor_lightning.png" title="monitor_lightning.png" alt="monitor lightning "/><em>monitor_lightning.png</em></td>

+		<td><img src="icons/monitor_link.png" title="monitor_link.png" alt="monitor link "/><em>monitor_link.png</em></td>

+		<td><img src="icons/mouse.png" title="mouse.png" alt="mouse "/><em>mouse.png</em></td>

+		<td><img src="icons/mouse_add.png" title="mouse_add.png" alt="mouse add "/><em>mouse_add.png</em></td>

+		<td><img src="icons/mouse_delete.png" title="mouse_delete.png" alt="mouse delete "/><em>mouse_delete.png</em></td>

+

+		<td><img src="icons/mouse_error.png" title="mouse_error.png" alt="mouse error "/><em>mouse_error.png</em></td>

+		<td><img src="icons/music.png" title="music.png" alt="music "/><em>music.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/new.png" title="new.png" alt="new "/><em>new.png</em></td>

+		<td><img src="icons/newspaper.png" title="newspaper.png" alt="newspaper "/><em>newspaper.png</em></td>

+		<td><img src="icons/newspaper_add.png" title="newspaper_add.png" alt="newspaper add "/><em>newspaper_add.png</em></td>

+

+		<td><img src="icons/newspaper_delete.png" title="newspaper_delete.png" alt="newspaper delete "/><em>newspaper_delete.png</em></td>

+		<td><img src="icons/newspaper_go.png" title="newspaper_go.png" alt="newspaper go "/><em>newspaper_go.png</em></td>

+		<td><img src="icons/newspaper_link.png" title="newspaper_link.png" alt="newspaper link "/><em>newspaper_link.png</em></td>

+		<td><img src="icons/note.png" title="note.png" alt="note "/><em>note.png</em></td>

+		<td><img src="icons/note_add.png" title="note_add.png" alt="note add "/><em>note_add.png</em></td>

+		<td><img src="icons/note_delete.png" title="note_delete.png" alt="note delete "/><em>note_delete.png</em></td>

+

+		<td><img src="icons/note_edit.png" title="note_edit.png" alt="note edit "/><em>note_edit.png</em></td>

+		<td><img src="icons/note_error.png" title="note_error.png" alt="note error "/><em>note_error.png</em></td>

+		<td><img src="icons/note_go.png" title="note_go.png" alt="note go "/><em>note_go.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/overlays.png" title="overlays.png" alt="overlays "/><em>overlays.png</em></td>

+		<td><img src="icons/package.png" title="package.png" alt="package "/><em>package.png</em></td>

+

+		<td><img src="icons/package_add.png" title="package_add.png" alt="package add "/><em>package_add.png</em></td>

+		<td><img src="icons/package_delete.png" title="package_delete.png" alt="package delete "/><em>package_delete.png</em></td>

+		<td><img src="icons/package_go.png" title="package_go.png" alt="package go "/><em>package_go.png</em></td>

+		<td><img src="icons/package_green.png" title="package_green.png" alt="package green "/><em>package_green.png</em></td>

+		<td><img src="icons/package_link.png" title="package_link.png" alt="package link "/><em>package_link.png</em></td>

+		<td><img src="icons/page.png" title="page.png" alt="page "/><em>page.png</em></td>

+

+		<td><img src="icons/page_add.png" title="page_add.png" alt="page add "/><em>page_add.png</em></td>

+		<td><img src="icons/page_attach.png" title="page_attach.png" alt="page attach "/><em>page_attach.png</em></td>

+		<td><img src="icons/page_code.png" title="page_code.png" alt="page code "/><em>page_code.png</em></td>

+		<td><img src="icons/page_copy.png" title="page_copy.png" alt="page copy "/><em>page_copy.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/page_delete.png" title="page_delete.png" alt="page delete "/><em>page_delete.png</em></td>

+

+		<td><img src="icons/page_edit.png" title="page_edit.png" alt="page edit "/><em>page_edit.png</em></td>

+		<td><img src="icons/page_error.png" title="page_error.png" alt="page error "/><em>page_error.png</em></td>

+		<td><img src="icons/page_excel.png" title="page_excel.png" alt="page excel "/><em>page_excel.png</em></td>

+		<td><img src="icons/page_find.png" title="page_find.png" alt="page find "/><em>page_find.png</em></td>

+		<td><img src="icons/page_gear.png" title="page_gear.png" alt="page gear "/><em>page_gear.png</em></td>

+		<td><img src="icons/page_go.png" title="page_go.png" alt="page go "/><em>page_go.png</em></td>

+

+		<td><img src="icons/page_green.png" title="page_green.png" alt="page green "/><em>page_green.png</em></td>

+		<td><img src="icons/page_key.png" title="page_key.png" alt="page key "/><em>page_key.png</em></td>

+		<td><img src="icons/page_lightning.png" title="page_lightning.png" alt="page lightning "/><em>page_lightning.png</em></td>

+		<td><img src="icons/page_link.png" title="page_link.png" alt="page link "/><em>page_link.png</em></td>

+		<td><img src="icons/page_paintbrush.png" title="page_paintbrush.png" alt="page paintbrush "/><em>page_paintbrush.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/page_paste.png" title="page_paste.png" alt="page paste "/><em>page_paste.png</em></td>

+		<td><img src="icons/page_red.png" title="page_red.png" alt="page red "/><em>page_red.png</em></td>

+		<td><img src="icons/page_refresh.png" title="page_refresh.png" alt="page refresh "/><em>page_refresh.png</em></td>

+		<td><img src="icons/page_save.png" title="page_save.png" alt="page save "/><em>page_save.png</em></td>

+		<td><img src="icons/page_white.png" title="page_white.png" alt="page white "/><em>page_white.png</em></td>

+

+		<td><img src="icons/page_white_acrobat.png" title="page_white_acrobat.png" alt="page white acrobat "/><em>page_white_acrobat.png</em></td>

+		<td><img src="icons/page_white_actionscript.png" title="page_white_actionscript.png" alt="page white actionscript "/><em>page_white_actionscript.png</em></td>

+		<td><img src="icons/page_white_add.png" title="page_white_add.png" alt="page white add "/><em>page_white_add.png</em></td>

+		<td><img src="icons/page_white_c.png" title="page_white_c.png" alt="page white c "/><em>page_white_c.png</em></td>

+		<td><img src="icons/page_white_camera.png" title="page_white_camera.png" alt="page white camera "/><em>page_white_camera.png</em></td>

+		<td><img src="icons/page_white_cd.png" title="page_white_cd.png" alt="page white cd "/><em>page_white_cd.png</em></td>

+

+		<td><img src="icons/page_white_code.png" title="page_white_code.png" alt="page white code "/><em>page_white_code.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/page_white_code_red.png" title="page_white_code_red.png" alt="page white code red "/><em>page_white_code_red.png</em></td>

+		<td><img src="icons/page_white_coldfusion.png" title="page_white_coldfusion.png" alt="page white coldfusion "/><em>page_white_coldfusion.png</em></td>

+		<td><img src="icons/page_white_compressed.png" title="page_white_compressed.png" alt="page white compressed "/><em>page_white_compressed.png</em></td>

+		<td><img src="icons/page_white_copy.png" title="page_white_copy.png" alt="page white copy "/><em>page_white_copy.png</em></td>

+

+		<td><img src="icons/page_white_cplusplus.png" title="page_white_cplusplus.png" alt="page white cplusplus "/><em>page_white_cplusplus.png</em></td>

+		<td><img src="icons/page_white_csharp.png" title="page_white_csharp.png" alt="page white csharp "/><em>page_white_csharp.png</em></td>

+		<td><img src="icons/page_white_cup.png" title="page_white_cup.png" alt="page white cup "/><em>page_white_cup.png</em></td>

+		<td><img src="icons/page_white_database.png" title="page_white_database.png" alt="page white database "/><em>page_white_database.png</em></td>

+		<td><img src="icons/page_white_delete.png" title="page_white_delete.png" alt="page white delete "/><em>page_white_delete.png</em></td>

+		<td><img src="icons/page_white_dvd.png" title="page_white_dvd.png" alt="page white dvd "/><em>page_white_dvd.png</em></td>

+

+		<td><img src="icons/page_white_edit.png" title="page_white_edit.png" alt="page white edit "/><em>page_white_edit.png</em></td>

+		<td><img src="icons/page_white_error.png" title="page_white_error.png" alt="page white error "/><em>page_white_error.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/page_white_excel.png" title="page_white_excel.png" alt="page white excel "/><em>page_white_excel.png</em></td>

+		<td><img src="icons/page_white_find.png" title="page_white_find.png" alt="page white find "/><em>page_white_find.png</em></td>

+		<td><img src="icons/page_white_flash.png" title="page_white_flash.png" alt="page white flash "/><em>page_white_flash.png</em></td>

+

+		<td><img src="icons/page_white_freehand.png" title="page_white_freehand.png" alt="page white freehand "/><em>page_white_freehand.png</em></td>

+		<td><img src="icons/page_white_gear.png" title="page_white_gear.png" alt="page white gear "/><em>page_white_gear.png</em></td>

+		<td><img src="icons/page_white_get.png" title="page_white_get.png" alt="page white get "/><em>page_white_get.png</em></td>

+		<td><img src="icons/page_white_go.png" title="page_white_go.png" alt="page white go "/><em>page_white_go.png</em></td>

+		<td><img src="icons/page_white_h.png" title="page_white_h.png" alt="page white h "/><em>page_white_h.png</em></td>

+		<td><img src="icons/page_white_horizontal.png" title="page_white_horizontal.png" alt="page white horizontal "/><em>page_white_horizontal.png</em></td>

+

+		<td><img src="icons/page_white_key.png" title="page_white_key.png" alt="page white key "/><em>page_white_key.png</em></td>

+		<td><img src="icons/page_white_lightning.png" title="page_white_lightning.png" alt="page white lightning "/><em>page_white_lightning.png</em></td>

+		<td><img src="icons/page_white_link.png" title="page_white_link.png" alt="page white link "/><em>page_white_link.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/page_white_magnify.png" title="page_white_magnify.png" alt="page white magnify "/><em>page_white_magnify.png</em></td>

+		<td><img src="icons/page_white_medal.png" title="page_white_medal.png" alt="page white medal "/><em>page_white_medal.png</em></td>

+

+		<td><img src="icons/page_white_office.png" title="page_white_office.png" alt="page white office "/><em>page_white_office.png</em></td>

+		<td><img src="icons/page_white_paint.png" title="page_white_paint.png" alt="page white paint "/><em>page_white_paint.png</em></td>

+		<td><img src="icons/page_white_paintbrush.png" title="page_white_paintbrush.png" alt="page white paintbrush "/><em>page_white_paintbrush.png</em></td>

+		<td><img src="icons/page_white_paste.png" title="page_white_paste.png" alt="page white paste "/><em>page_white_paste.png</em></td>

+		<td><img src="icons/page_white_php.png" title="page_white_php.png" alt="page white php "/><em>page_white_php.png</em></td>

+		<td><img src="icons/page_white_picture.png" title="page_white_picture.png" alt="page white picture "/><em>page_white_picture.png</em></td>

+

+		<td><img src="icons/page_white_powerpoint.png" title="page_white_powerpoint.png" alt="page white powerpoint "/><em>page_white_powerpoint.png</em></td>

+		<td><img src="icons/page_white_put.png" title="page_white_put.png" alt="page white put "/><em>page_white_put.png</em></td>

+		<td><img src="icons/page_white_ruby.png" title="page_white_ruby.png" alt="page white ruby "/><em>page_white_ruby.png</em></td>

+		<td><img src="icons/page_white_stack.png" title="page_white_stack.png" alt="page white stack "/><em>page_white_stack.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/page_white_star.png" title="page_white_star.png" alt="page white star "/><em>page_white_star.png</em></td>

+

+		<td><img src="icons/page_white_swoosh.png" title="page_white_swoosh.png" alt="page white swoosh "/><em>page_white_swoosh.png</em></td>

+		<td><img src="icons/page_white_text.png" title="page_white_text.png" alt="page white text "/><em>page_white_text.png</em></td>

+		<td><img src="icons/page_white_text_width.png" title="page_white_text_width.png" alt="page white text width "/><em>page_white_text_width.png</em></td>

+		<td><img src="icons/page_white_tux.png" title="page_white_tux.png" alt="page white tux "/><em>page_white_tux.png</em></td>

+		<td><img src="icons/page_white_vector.png" title="page_white_vector.png" alt="page white vector "/><em>page_white_vector.png</em></td>

+		<td><img src="icons/page_white_visualstudio.png" title="page_white_visualstudio.png" alt="page white visualstudio "/><em>page_white_visualstudio.png</em></td>

+

+		<td><img src="icons/page_white_width.png" title="page_white_width.png" alt="page white width "/><em>page_white_width.png</em></td>

+		<td><img src="icons/page_white_word.png" title="page_white_word.png" alt="page white word "/><em>page_white_word.png</em></td>

+		<td><img src="icons/page_white_world.png" title="page_white_world.png" alt="page white world "/><em>page_white_world.png</em></td>

+		<td><img src="icons/page_white_wrench.png" title="page_white_wrench.png" alt="page white wrench "/><em>page_white_wrench.png</em></td>

+		<td><img src="icons/page_white_zip.png" title="page_white_zip.png" alt="page white zip "/><em>page_white_zip.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/page_word.png" title="page_word.png" alt="page word "/><em>page_word.png</em></td>

+		<td><img src="icons/page_world.png" title="page_world.png" alt="page world "/><em>page_world.png</em></td>

+		<td><img src="icons/paintbrush.png" title="paintbrush.png" alt="paintbrush "/><em>paintbrush.png</em></td>

+		<td><img src="icons/paintcan.png" title="paintcan.png" alt="paintcan "/><em>paintcan.png</em></td>

+		<td><img src="icons/palette.png" title="palette.png" alt="palette "/><em>palette.png</em></td>

+

+		<td><img src="icons/paste_plain.png" title="paste_plain.png" alt="paste plain "/><em>paste_plain.png</em></td>

+		<td><img src="icons/paste_word.png" title="paste_word.png" alt="paste word "/><em>paste_word.png</em></td>

+		<td><img src="icons/pencil.png" title="pencil.png" alt="pencil "/><em>pencil.png</em></td>

+		<td><img src="icons/pencil_add.png" title="pencil_add.png" alt="pencil add "/><em>pencil_add.png</em></td>

+		<td><img src="icons/pencil_delete.png" title="pencil_delete.png" alt="pencil delete "/><em>pencil_delete.png</em></td>

+		<td><img src="icons/pencil_go.png" title="pencil_go.png" alt="pencil go "/><em>pencil_go.png</em></td>

+

+		<td><img src="icons/phone.png" title="phone.png" alt="phone "/><em>phone.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/phone_add.png" title="phone_add.png" alt="phone add "/><em>phone_add.png</em></td>

+		<td><img src="icons/phone_delete.png" title="phone_delete.png" alt="phone delete "/><em>phone_delete.png</em></td>

+		<td><img src="icons/phone_sound.png" title="phone_sound.png" alt="phone sound "/><em>phone_sound.png</em></td>

+		<td><img src="icons/photo.png" title="photo.png" alt="photo "/><em>photo.png</em></td>

+

+		<td><img src="icons/photo_add.png" title="photo_add.png" alt="photo add "/><em>photo_add.png</em></td>

+		<td><img src="icons/photo_delete.png" title="photo_delete.png" alt="photo delete "/><em>photo_delete.png</em></td>

+		<td><img src="icons/photo_link.png" title="photo_link.png" alt="photo link "/><em>photo_link.png</em></td>

+		<td><img src="icons/photos.png" title="photos.png" alt="photos "/><em>photos.png</em></td>

+		<td><img src="icons/picture.png" title="picture.png" alt="picture "/><em>picture.png</em></td>

+		<td><img src="icons/picture_add.png" title="picture_add.png" alt="picture add "/><em>picture_add.png</em></td>

+

+		<td><img src="icons/picture_delete.png" title="picture_delete.png" alt="picture delete "/><em>picture_delete.png</em></td>

+		<td><img src="icons/picture_edit.png" title="picture_edit.png" alt="picture edit "/><em>picture_edit.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/picture_empty.png" title="picture_empty.png" alt="picture empty "/><em>picture_empty.png</em></td>

+		<td><img src="icons/picture_error.png" title="picture_error.png" alt="picture error "/><em>picture_error.png</em></td>

+		<td><img src="icons/picture_go.png" title="picture_go.png" alt="picture go "/><em>picture_go.png</em></td>

+

+		<td><img src="icons/picture_key.png" title="picture_key.png" alt="picture key "/><em>picture_key.png</em></td>

+		<td><img src="icons/picture_link.png" title="picture_link.png" alt="picture link "/><em>picture_link.png</em></td>

+		<td><img src="icons/picture_save.png" title="picture_save.png" alt="picture save "/><em>picture_save.png</em></td>

+		<td><img src="icons/pictures.png" title="pictures.png" alt="pictures "/><em>pictures.png</em></td>

+		<td><img src="icons/pilcrow.png" title="pilcrow.png" alt="pilcrow "/><em>pilcrow.png</em></td>

+		<td><img src="icons/pill.png" title="pill.png" alt="pill "/><em>pill.png</em></td>

+

+		<td><img src="icons/pill_add.png" title="pill_add.png" alt="pill add "/><em>pill_add.png</em></td>

+		<td><img src="icons/pill_delete.png" title="pill_delete.png" alt="pill delete "/><em>pill_delete.png</em></td>

+		<td><img src="icons/pill_go.png" title="pill_go.png" alt="pill go "/><em>pill_go.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/plugin.png" title="plugin.png" alt="plugin "/><em>plugin.png</em></td>

+		<td><img src="icons/plugin_add.png" title="plugin_add.png" alt="plugin add "/><em>plugin_add.png</em></td>

+

+		<td><img src="icons/plugin_delete.png" title="plugin_delete.png" alt="plugin delete "/><em>plugin_delete.png</em></td>

+		<td><img src="icons/plugin_disabled.png" title="plugin_disabled.png" alt="plugin disabled "/><em>plugin_disabled.png</em></td>

+		<td><img src="icons/plugin_edit.png" title="plugin_edit.png" alt="plugin edit "/><em>plugin_edit.png</em></td>

+		<td><img src="icons/plugin_error.png" title="plugin_error.png" alt="plugin error "/><em>plugin_error.png</em></td>

+		<td><img src="icons/plugin_go.png" title="plugin_go.png" alt="plugin go "/><em>plugin_go.png</em></td>

+		<td><img src="icons/plugin_link.png" title="plugin_link.png" alt="plugin link "/><em>plugin_link.png</em></td>

+

+		<td><img src="icons/printer.png" title="printer.png" alt="printer "/><em>printer.png</em></td>

+		<td><img src="icons/printer_add.png" title="printer_add.png" alt="printer add "/><em>printer_add.png</em></td>

+		<td><img src="icons/printer_delete.png" title="printer_delete.png" alt="printer delete "/><em>printer_delete.png</em></td>

+		<td><img src="icons/printer_empty.png" title="printer_empty.png" alt="printer empty "/><em>printer_empty.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/printer_error.png" title="printer_error.png" alt="printer error "/><em>printer_error.png</em></td>

+

+		<td><img src="icons/rainbow.png" title="rainbow.png" alt="rainbow "/><em>rainbow.png</em></td>

+		<td><img src="icons/report.png" title="report.png" alt="report "/><em>report.png</em></td>

+		<td><img src="icons/report_add.png" title="report_add.png" alt="report add "/><em>report_add.png</em></td>

+		<td><img src="icons/report_delete.png" title="report_delete.png" alt="report delete "/><em>report_delete.png</em></td>

+		<td><img src="icons/report_disk.png" title="report_disk.png" alt="report disk "/><em>report_disk.png</em></td>

+		<td><img src="icons/report_edit.png" title="report_edit.png" alt="report edit "/><em>report_edit.png</em></td>

+

+		<td><img src="icons/report_go.png" title="report_go.png" alt="report go "/><em>report_go.png</em></td>

+		<td><img src="icons/report_key.png" title="report_key.png" alt="report key "/><em>report_key.png</em></td>

+		<td><img src="icons/report_link.png" title="report_link.png" alt="report link "/><em>report_link.png</em></td>

+		<td><img src="icons/report_magnify.png" title="report_magnify.png" alt="report magnify "/><em>report_magnify.png</em></td>

+		<td><img src="icons/report_picture.png" title="report_picture.png" alt="report picture "/><em>report_picture.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/report_user.png" title="report_user.png" alt="report user "/><em>report_user.png</em></td>

+		<td><img src="icons/report_word.png" title="report_word.png" alt="report word "/><em>report_word.png</em></td>

+		<td><img src="icons/resultset_first.png" title="resultset_first.png" alt="resultset first "/><em>resultset_first.png</em></td>

+		<td><img src="icons/resultset_last.png" title="resultset_last.png" alt="resultset last "/><em>resultset_last.png</em></td>

+		<td><img src="icons/resultset_next.png" title="resultset_next.png" alt="resultset next "/><em>resultset_next.png</em></td>

+

+		<td><img src="icons/resultset_previous.png" title="resultset_previous.png" alt="resultset previous "/><em>resultset_previous.png</em></td>

+		<td><img src="icons/rosette.png" title="rosette.png" alt="rosette "/><em>rosette.png</em></td>

+		<td><img src="icons/rss.png" title="rss.png" alt="rss "/><em>rss.png</em></td>

+		<td><img src="icons/rss_add.png" title="rss_add.png" alt="rss add "/><em>rss_add.png</em></td>

+		<td><img src="icons/rss_delete.png" title="rss_delete.png" alt="rss delete "/><em>rss_delete.png</em></td>

+		<td><img src="icons/rss_go.png" title="rss_go.png" alt="rss go "/><em>rss_go.png</em></td>

+

+		<td><img src="icons/rss_valid.png" title="rss_valid.png" alt="rss valid "/><em>rss_valid.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/ruby.png" title="ruby.png" alt="ruby "/><em>ruby.png</em></td>

+		<td><img src="icons/ruby_add.png" title="ruby_add.png" alt="ruby add "/><em>ruby_add.png</em></td>

+		<td><img src="icons/ruby_delete.png" title="ruby_delete.png" alt="ruby delete "/><em>ruby_delete.png</em></td>

+		<td><img src="icons/ruby_gear.png" title="ruby_gear.png" alt="ruby gear "/><em>ruby_gear.png</em></td>

+

+		<td><img src="icons/ruby_get.png" title="ruby_get.png" alt="ruby get "/><em>ruby_get.png</em></td>

+		<td><img src="icons/ruby_go.png" title="ruby_go.png" alt="ruby go "/><em>ruby_go.png</em></td>

+		<td><img src="icons/ruby_key.png" title="ruby_key.png" alt="ruby key "/><em>ruby_key.png</em></td>

+		<td><img src="icons/ruby_link.png" title="ruby_link.png" alt="ruby link "/><em>ruby_link.png</em></td>

+		<td><img src="icons/ruby_put.png" title="ruby_put.png" alt="ruby put "/><em>ruby_put.png</em></td>

+		<td><img src="icons/script.png" title="script.png" alt="script "/><em>script.png</em></td>

+

+		<td><img src="icons/script_add.png" title="script_add.png" alt="script add "/><em>script_add.png</em></td>

+		<td><img src="icons/script_code.png" title="script_code.png" alt="script code "/><em>script_code.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/script_code_red.png" title="script_code_red.png" alt="script code red "/><em>script_code_red.png</em></td>

+		<td><img src="icons/script_delete.png" title="script_delete.png" alt="script delete "/><em>script_delete.png</em></td>

+		<td><img src="icons/script_edit.png" title="script_edit.png" alt="script edit "/><em>script_edit.png</em></td>

+

+		<td><img src="icons/script_error.png" title="script_error.png" alt="script error "/><em>script_error.png</em></td>

+		<td><img src="icons/script_gear.png" title="script_gear.png" alt="script gear "/><em>script_gear.png</em></td>

+		<td><img src="icons/script_go.png" title="script_go.png" alt="script go "/><em>script_go.png</em></td>

+		<td><img src="icons/script_key.png" title="script_key.png" alt="script key "/><em>script_key.png</em></td>

+		<td><img src="icons/script_lightning.png" title="script_lightning.png" alt="script lightning "/><em>script_lightning.png</em></td>

+		<td><img src="icons/script_link.png" title="script_link.png" alt="script link "/><em>script_link.png</em></td>

+

+		<td><img src="icons/script_palette.png" title="script_palette.png" alt="script palette "/><em>script_palette.png</em></td>

+		<td><img src="icons/script_save.png" title="script_save.png" alt="script save "/><em>script_save.png</em></td>

+		<td><img src="icons/server.png" title="server.png" alt="server "/><em>server.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/server_add.png" title="server_add.png" alt="server add "/><em>server_add.png</em></td>

+		<td><img src="icons/server_chart.png" title="server_chart.png" alt="server chart "/><em>server_chart.png</em></td>

+

+		<td><img src="icons/server_compressed.png" title="server_compressed.png" alt="server compressed "/><em>server_compressed.png</em></td>

+		<td><img src="icons/server_connect.png" title="server_connect.png" alt="server connect "/><em>server_connect.png</em></td>

+		<td><img src="icons/server_database.png" title="server_database.png" alt="server database "/><em>server_database.png</em></td>

+		<td><img src="icons/server_delete.png" title="server_delete.png" alt="server delete "/><em>server_delete.png</em></td>

+		<td><img src="icons/server_edit.png" title="server_edit.png" alt="server edit "/><em>server_edit.png</em></td>

+		<td><img src="icons/server_error.png" title="server_error.png" alt="server error "/><em>server_error.png</em></td>

+

+		<td><img src="icons/server_go.png" title="server_go.png" alt="server go "/><em>server_go.png</em></td>

+		<td><img src="icons/server_key.png" title="server_key.png" alt="server key "/><em>server_key.png</em></td>

+		<td><img src="icons/server_lightning.png" title="server_lightning.png" alt="server lightning "/><em>server_lightning.png</em></td>

+		<td><img src="icons/server_link.png" title="server_link.png" alt="server link "/><em>server_link.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/server_uncompressed.png" title="server_uncompressed.png" alt="server uncompressed "/><em>server_uncompressed.png</em></td>

+

+		<td><img src="icons/shading.png" title="shading.png" alt="shading "/><em>shading.png</em></td>

+		<td><img src="icons/shape_align_bottom.png" title="shape_align_bottom.png" alt="shape align bottom "/><em>shape_align_bottom.png</em></td>

+		<td><img src="icons/shape_align_center.png" title="shape_align_center.png" alt="shape align center "/><em>shape_align_center.png</em></td>

+		<td><img src="icons/shape_align_left.png" title="shape_align_left.png" alt="shape align left "/><em>shape_align_left.png</em></td>

+		<td><img src="icons/shape_align_middle.png" title="shape_align_middle.png" alt="shape align middle "/><em>shape_align_middle.png</em></td>

+		<td><img src="icons/shape_align_right.png" title="shape_align_right.png" alt="shape align right "/><em>shape_align_right.png</em></td>

+

+		<td><img src="icons/shape_align_top.png" title="shape_align_top.png" alt="shape align top "/><em>shape_align_top.png</em></td>

+		<td><img src="icons/shape_flip_horizontal.png" title="shape_flip_horizontal.png" alt="shape flip horizontal "/><em>shape_flip_horizontal.png</em></td>

+		<td><img src="icons/shape_flip_vertical.png" title="shape_flip_vertical.png" alt="shape flip vertical "/><em>shape_flip_vertical.png</em></td>

+		<td><img src="icons/shape_group.png" title="shape_group.png" alt="shape group "/><em>shape_group.png</em></td>

+		<td><img src="icons/shape_handles.png" title="shape_handles.png" alt="shape handles "/><em>shape_handles.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/shape_move_back.png" title="shape_move_back.png" alt="shape move back "/><em>shape_move_back.png</em></td>

+		<td><img src="icons/shape_move_backwards.png" title="shape_move_backwards.png" alt="shape move backwards "/><em>shape_move_backwards.png</em></td>

+		<td><img src="icons/shape_move_forwards.png" title="shape_move_forwards.png" alt="shape move forwards "/><em>shape_move_forwards.png</em></td>

+		<td><img src="icons/shape_move_front.png" title="shape_move_front.png" alt="shape move front "/><em>shape_move_front.png</em></td>

+		<td><img src="icons/shape_rotate_anticlockwise.png" title="shape_rotate_anticlockwise.png" alt="shape rotate anticlockwise "/><em>shape_rotate_anticlockwise.png</em></td>

+

+		<td><img src="icons/shape_rotate_clockwise.png" title="shape_rotate_clockwise.png" alt="shape rotate clockwise "/><em>shape_rotate_clockwise.png</em></td>

+		<td><img src="icons/shape_square.png" title="shape_square.png" alt="shape square "/><em>shape_square.png</em></td>

+		<td><img src="icons/shape_square_add.png" title="shape_square_add.png" alt="shape square add "/><em>shape_square_add.png</em></td>

+		<td><img src="icons/shape_square_delete.png" title="shape_square_delete.png" alt="shape square delete "/><em>shape_square_delete.png</em></td>

+		<td><img src="icons/shape_square_edit.png" title="shape_square_edit.png" alt="shape square edit "/><em>shape_square_edit.png</em></td>

+		<td><img src="icons/shape_square_error.png" title="shape_square_error.png" alt="shape square error "/><em>shape_square_error.png</em></td>

+

+		<td><img src="icons/shape_square_go.png" title="shape_square_go.png" alt="shape square go "/><em>shape_square_go.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/shape_square_key.png" title="shape_square_key.png" alt="shape square key "/><em>shape_square_key.png</em></td>

+		<td><img src="icons/shape_square_link.png" title="shape_square_link.png" alt="shape square link "/><em>shape_square_link.png</em></td>

+		<td><img src="icons/shape_ungroup.png" title="shape_ungroup.png" alt="shape ungroup "/><em>shape_ungroup.png</em></td>

+		<td><img src="icons/shield.png" title="shield.png" alt="shield "/><em>shield.png</em></td>

+

+		<td><img src="icons/shield_add.png" title="shield_add.png" alt="shield add "/><em>shield_add.png</em></td>

+		<td><img src="icons/shield_delete.png" title="shield_delete.png" alt="shield delete "/><em>shield_delete.png</em></td>

+		<td><img src="icons/shield_go.png" title="shield_go.png" alt="shield go "/><em>shield_go.png</em></td>

+		<td><img src="icons/sitemap.png" title="sitemap.png" alt="sitemap "/><em>sitemap.png</em></td>

+		<td><img src="icons/sitemap_color.png" title="sitemap_color.png" alt="sitemap color "/><em>sitemap_color.png</em></td>

+		<td><img src="icons/sound.png" title="sound.png" alt="sound "/><em>sound.png</em></td>

+

+		<td><img src="icons/sound_add.png" title="sound_add.png" alt="sound add "/><em>sound_add.png</em></td>

+		<td><img src="icons/sound_delete.png" title="sound_delete.png" alt="sound delete "/><em>sound_delete.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/sound_low.png" title="sound_low.png" alt="sound low "/><em>sound_low.png</em></td>

+		<td><img src="icons/sound_mute.png" title="sound_mute.png" alt="sound mute "/><em>sound_mute.png</em></td>

+		<td><img src="icons/sound_none.png" title="sound_none.png" alt="sound none "/><em>sound_none.png</em></td>

+

+		<td><img src="icons/spellcheck.png" title="spellcheck.png" alt="spellcheck "/><em>spellcheck.png</em></td>

+		<td><img src="icons/sport_8ball.png" title="sport_8ball.png" alt="sport 8ball "/><em>sport_8ball.png</em></td>

+		<td><img src="icons/sport_basketball.png" title="sport_basketball.png" alt="sport basketball "/><em>sport_basketball.png</em></td>

+		<td><img src="icons/sport_football.png" title="sport_football.png" alt="sport football "/><em>sport_football.png</em></td>

+		<td><img src="icons/sport_golf.png" title="sport_golf.png" alt="sport golf "/><em>sport_golf.png</em></td>

+		<td><img src="icons/sport_raquet.png" title="sport_raquet.png" alt="sport raquet "/><em>sport_raquet.png</em></td>

+

+		<td><img src="icons/sport_shuttlecock.png" title="sport_shuttlecock.png" alt="sport shuttlecock "/><em>sport_shuttlecock.png</em></td>

+		<td><img src="icons/sport_soccer.png" title="sport_soccer.png" alt="sport soccer "/><em>sport_soccer.png</em></td>

+		<td><img src="icons/sport_tennis.png" title="sport_tennis.png" alt="sport tennis "/><em>sport_tennis.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/star.png" title="star.png" alt="star "/><em>star.png</em></td>

+		<td><img src="icons/status_away.png" title="status_away.png" alt="status away "/><em>status_away.png</em></td>

+

+		<td><img src="icons/status_busy.png" title="status_busy.png" alt="status busy "/><em>status_busy.png</em></td>

+		<td><img src="icons/status_offline.png" title="status_offline.png" alt="status offline "/><em>status_offline.png</em></td>

+		<td><img src="icons/status_online.png" title="status_online.png" alt="status online "/><em>status_online.png</em></td>

+		<td><img src="icons/stop.png" title="stop.png" alt="stop "/><em>stop.png</em></td>

+		<td><img src="icons/style.png" title="style.png" alt="style "/><em>style.png</em></td>

+		<td><img src="icons/style_add.png" title="style_add.png" alt="style add "/><em>style_add.png</em></td>

+

+		<td><img src="icons/style_delete.png" title="style_delete.png" alt="style delete "/><em>style_delete.png</em></td>

+		<td><img src="icons/style_edit.png" title="style_edit.png" alt="style edit "/><em>style_edit.png</em></td>

+		<td><img src="icons/style_go.png" title="style_go.png" alt="style go "/><em>style_go.png</em></td>

+		<td><img src="icons/sum.png" title="sum.png" alt="sum "/><em>sum.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/tab.png" title="tab.png" alt="tab "/><em>tab.png</em></td>

+

+		<td><img src="icons/tab_add.png" title="tab_add.png" alt="tab add "/><em>tab_add.png</em></td>

+		<td><img src="icons/tab_delete.png" title="tab_delete.png" alt="tab delete "/><em>tab_delete.png</em></td>

+		<td><img src="icons/tab_edit.png" title="tab_edit.png" alt="tab edit "/><em>tab_edit.png</em></td>

+		<td><img src="icons/tab_go.png" title="tab_go.png" alt="tab go "/><em>tab_go.png</em></td>

+		<td><img src="icons/table.png" title="table.png" alt="table "/><em>table.png</em></td>

+		<td><img src="icons/table_add.png" title="table_add.png" alt="table add "/><em>table_add.png</em></td>

+

+		<td><img src="icons/table_delete.png" title="table_delete.png" alt="table delete "/><em>table_delete.png</em></td>

+		<td><img src="icons/table_edit.png" title="table_edit.png" alt="table edit "/><em>table_edit.png</em></td>

+		<td><img src="icons/table_error.png" title="table_error.png" alt="table error "/><em>table_error.png</em></td>

+		<td><img src="icons/table_gear.png" title="table_gear.png" alt="table gear "/><em>table_gear.png</em></td>

+		<td><img src="icons/table_go.png" title="table_go.png" alt="table go "/><em>table_go.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/table_key.png" title="table_key.png" alt="table key "/><em>table_key.png</em></td>

+		<td><img src="icons/table_lightning.png" title="table_lightning.png" alt="table lightning "/><em>table_lightning.png</em></td>

+		<td><img src="icons/table_link.png" title="table_link.png" alt="table link "/><em>table_link.png</em></td>

+		<td><img src="icons/table_multiple.png" title="table_multiple.png" alt="table multiple "/><em>table_multiple.png</em></td>

+		<td><img src="icons/table_refresh.png" title="table_refresh.png" alt="table refresh "/><em>table_refresh.png</em></td>

+

+		<td><img src="icons/table_relationship.png" title="table_relationship.png" alt="table relationship "/><em>table_relationship.png</em></td>

+		<td><img src="icons/table_row_delete.png" title="table_row_delete.png" alt="table row delete "/><em>table_row_delete.png</em></td>

+		<td><img src="icons/table_row_insert.png" title="table_row_insert.png" alt="table row insert "/><em>table_row_insert.png</em></td>

+		<td><img src="icons/table_save.png" title="table_save.png" alt="table save "/><em>table_save.png</em></td>

+		<td><img src="icons/table_sort.png" title="table_sort.png" alt="table sort "/><em>table_sort.png</em></td>

+		<td><img src="icons/tag.png" title="tag.png" alt="tag "/><em>tag.png</em></td>

+

+		<td><img src="icons/tag_blue.png" title="tag_blue.png" alt="tag blue "/><em>tag_blue.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/tag_blue_add.png" title="tag_blue_add.png" alt="tag blue add "/><em>tag_blue_add.png</em></td>

+		<td><img src="icons/tag_blue_delete.png" title="tag_blue_delete.png" alt="tag blue delete "/><em>tag_blue_delete.png</em></td>

+		<td><img src="icons/tag_blue_edit.png" title="tag_blue_edit.png" alt="tag blue edit "/><em>tag_blue_edit.png</em></td>

+		<td><img src="icons/tag_green.png" title="tag_green.png" alt="tag green "/><em>tag_green.png</em></td>

+

+		<td><img src="icons/tag_orange.png" title="tag_orange.png" alt="tag orange "/><em>tag_orange.png</em></td>

+		<td><img src="icons/tag_pink.png" title="tag_pink.png" alt="tag pink "/><em>tag_pink.png</em></td>

+		<td><img src="icons/tag_purple.png" title="tag_purple.png" alt="tag purple "/><em>tag_purple.png</em></td>

+		<td><img src="icons/tag_red.png" title="tag_red.png" alt="tag red "/><em>tag_red.png</em></td>

+		<td><img src="icons/tag_yellow.png" title="tag_yellow.png" alt="tag yellow "/><em>tag_yellow.png</em></td>

+		<td><img src="icons/telephone.png" title="telephone.png" alt="telephone "/><em>telephone.png</em></td>

+

+		<td><img src="icons/telephone_add.png" title="telephone_add.png" alt="telephone add "/><em>telephone_add.png</em></td>

+		<td><img src="icons/telephone_delete.png" title="telephone_delete.png" alt="telephone delete "/><em>telephone_delete.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/telephone_edit.png" title="telephone_edit.png" alt="telephone edit "/><em>telephone_edit.png</em></td>

+		<td><img src="icons/telephone_error.png" title="telephone_error.png" alt="telephone error "/><em>telephone_error.png</em></td>

+		<td><img src="icons/telephone_go.png" title="telephone_go.png" alt="telephone go "/><em>telephone_go.png</em></td>

+

+		<td><img src="icons/telephone_key.png" title="telephone_key.png" alt="telephone key "/><em>telephone_key.png</em></td>

+		<td><img src="icons/telephone_link.png" title="telephone_link.png" alt="telephone link "/><em>telephone_link.png</em></td>

+		<td><img src="icons/television.png" title="television.png" alt="television "/><em>television.png</em></td>

+		<td><img src="icons/television_add.png" title="television_add.png" alt="television add "/><em>television_add.png</em></td>

+		<td><img src="icons/television_delete.png" title="television_delete.png" alt="television delete "/><em>television_delete.png</em></td>

+		<td><img src="icons/text_align_center.png" title="text_align_center.png" alt="text align center "/><em>text_align_center.png</em></td>

+

+		<td><img src="icons/text_align_justify.png" title="text_align_justify.png" alt="text align justify "/><em>text_align_justify.png</em></td>

+		<td><img src="icons/text_align_left.png" title="text_align_left.png" alt="text align left "/><em>text_align_left.png</em></td>

+		<td><img src="icons/text_align_right.png" title="text_align_right.png" alt="text align right "/><em>text_align_right.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/text_allcaps.png" title="text_allcaps.png" alt="text allcaps "/><em>text_allcaps.png</em></td>

+		<td><img src="icons/text_bold.png" title="text_bold.png" alt="text bold "/><em>text_bold.png</em></td>

+

+		<td><img src="icons/text_columns.png" title="text_columns.png" alt="text columns "/><em>text_columns.png</em></td>

+		<td><img src="icons/text_dropcaps.png" title="text_dropcaps.png" alt="text dropcaps "/><em>text_dropcaps.png</em></td>

+		<td><img src="icons/text_heading_1.png" title="text_heading_1.png" alt="text heading 1 "/><em>text_heading_1.png</em></td>

+		<td><img src="icons/text_heading_2.png" title="text_heading_2.png" alt="text heading 2 "/><em>text_heading_2.png</em></td>

+		<td><img src="icons/text_heading_3.png" title="text_heading_3.png" alt="text heading 3 "/><em>text_heading_3.png</em></td>

+		<td><img src="icons/text_heading_4.png" title="text_heading_4.png" alt="text heading 4 "/><em>text_heading_4.png</em></td>

+

+		<td><img src="icons/text_heading_5.png" title="text_heading_5.png" alt="text heading 5 "/><em>text_heading_5.png</em></td>

+		<td><img src="icons/text_heading_6.png" title="text_heading_6.png" alt="text heading 6 "/><em>text_heading_6.png</em></td>

+		<td><img src="icons/text_horizontalrule.png" title="text_horizontalrule.png" alt="text horizontalrule "/><em>text_horizontalrule.png</em></td>

+		<td><img src="icons/text_indent.png" title="text_indent.png" alt="text indent "/><em>text_indent.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/text_indent_remove.png" title="text_indent_remove.png" alt="text indent remove "/><em>text_indent_remove.png</em></td>

+

+		<td><img src="icons/text_italic.png" title="text_italic.png" alt="text italic "/><em>text_italic.png</em></td>

+		<td><img src="icons/text_kerning.png" title="text_kerning.png" alt="text kerning "/><em>text_kerning.png</em></td>

+		<td><img src="icons/text_letter_omega.png" title="text_letter_omega.png" alt="text letter omega "/><em>text_letter_omega.png</em></td>

+		<td><img src="icons/text_letterspacing.png" title="text_letterspacing.png" alt="text letterspacing "/><em>text_letterspacing.png</em></td>

+		<td><img src="icons/text_linespacing.png" title="text_linespacing.png" alt="text linespacing "/><em>text_linespacing.png</em></td>

+		<td><img src="icons/text_list_bullets.png" title="text_list_bullets.png" alt="text list bullets "/><em>text_list_bullets.png</em></td>

+

+		<td><img src="icons/text_list_numbers.png" title="text_list_numbers.png" alt="text list numbers "/><em>text_list_numbers.png</em></td>

+		<td><img src="icons/text_lowercase.png" title="text_lowercase.png" alt="text lowercase "/><em>text_lowercase.png</em></td>

+		<td><img src="icons/text_padding_bottom.png" title="text_padding_bottom.png" alt="text padding bottom "/><em>text_padding_bottom.png</em></td>

+		<td><img src="icons/text_padding_left.png" title="text_padding_left.png" alt="text padding left "/><em>text_padding_left.png</em></td>

+		<td><img src="icons/text_padding_right.png" title="text_padding_right.png" alt="text padding right "/><em>text_padding_right.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/text_padding_top.png" title="text_padding_top.png" alt="text padding top "/><em>text_padding_top.png</em></td>

+		<td><img src="icons/text_replace.png" title="text_replace.png" alt="text replace "/><em>text_replace.png</em></td>

+		<td><img src="icons/text_signature.png" title="text_signature.png" alt="text signature "/><em>text_signature.png</em></td>

+		<td><img src="icons/text_smallcaps.png" title="text_smallcaps.png" alt="text smallcaps "/><em>text_smallcaps.png</em></td>

+		<td><img src="icons/text_strikethrough.png" title="text_strikethrough.png" alt="text strikethrough "/><em>text_strikethrough.png</em></td>

+

+		<td><img src="icons/text_subscript.png" title="text_subscript.png" alt="text subscript "/><em>text_subscript.png</em></td>

+		<td><img src="icons/text_superscript.png" title="text_superscript.png" alt="text superscript "/><em>text_superscript.png</em></td>

+		<td><img src="icons/text_underline.png" title="text_underline.png" alt="text underline "/><em>text_underline.png</em></td>

+		<td><img src="icons/text_uppercase.png" title="text_uppercase.png" alt="text uppercase "/><em>text_uppercase.png</em></td>

+		<td><img src="icons/textfield.png" title="textfield.png" alt="textfield "/><em>textfield.png</em></td>

+		<td><img src="icons/textfield_add.png" title="textfield_add.png" alt="textfield add "/><em>textfield_add.png</em></td>

+

+		<td><img src="icons/textfield_delete.png" title="textfield_delete.png" alt="textfield delete "/><em>textfield_delete.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/textfield_key.png" title="textfield_key.png" alt="textfield key "/><em>textfield_key.png</em></td>

+		<td><img src="icons/textfield_rename.png" title="textfield_rename.png" alt="textfield rename "/><em>textfield_rename.png</em></td>

+		<td><img src="icons/thumb_down.png" title="thumb_down.png" alt="thumb down "/><em>thumb_down.png</em></td>

+		<td><img src="icons/thumb_up.png" title="thumb_up.png" alt="thumb up "/><em>thumb_up.png</em></td>

+

+		<td><img src="icons/tick.png" title="tick.png" alt="tick "/><em>tick.png</em></td>

+		<td><img src="icons/time.png" title="time.png" alt="time "/><em>time.png</em></td>

+		<td><img src="icons/time_add.png" title="time_add.png" alt="time add "/><em>time_add.png</em></td>

+		<td><img src="icons/time_delete.png" title="time_delete.png" alt="time delete "/><em>time_delete.png</em></td>

+		<td><img src="icons/time_go.png" title="time_go.png" alt="time go "/><em>time_go.png</em></td>

+		<td><img src="icons/timeline_marker.png" title="timeline_marker.png" alt="timeline marker "/><em>timeline_marker.png</em></td>

+

+		<td><img src="icons/transmit.png" title="transmit.png" alt="transmit "/><em>transmit.png</em></td>

+		<td><img src="icons/transmit_add.png" title="transmit_add.png" alt="transmit add "/><em>transmit_add.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/transmit_blue.png" title="transmit_blue.png" alt="transmit blue "/><em>transmit_blue.png</em></td>

+		<td><img src="icons/transmit_delete.png" title="transmit_delete.png" alt="transmit delete "/><em>transmit_delete.png</em></td>

+		<td><img src="icons/transmit_edit.png" title="transmit_edit.png" alt="transmit edit "/><em>transmit_edit.png</em></td>

+

+		<td><img src="icons/transmit_error.png" title="transmit_error.png" alt="transmit error "/><em>transmit_error.png</em></td>

+		<td><img src="icons/transmit_go.png" title="transmit_go.png" alt="transmit go "/><em>transmit_go.png</em></td>

+		<td><img src="icons/tux.png" title="tux.png" alt="tux "/><em>tux.png</em></td>

+		<td><img src="icons/user.png" title="user.png" alt="user "/><em>user.png</em></td>

+		<td><img src="icons/user_add.png" title="user_add.png" alt="user add "/><em>user_add.png</em></td>

+		<td><img src="icons/user_comment.png" title="user_comment.png" alt="user comment "/><em>user_comment.png</em></td>

+

+		<td><img src="icons/user_delete.png" title="user_delete.png" alt="user delete "/><em>user_delete.png</em></td>

+		<td><img src="icons/user_edit.png" title="user_edit.png" alt="user edit "/><em>user_edit.png</em></td>

+		<td><img src="icons/user_female.png" title="user_female.png" alt="user female "/><em>user_female.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/user_go.png" title="user_go.png" alt="user go "/><em>user_go.png</em></td>

+		<td><img src="icons/user_gray.png" title="user_gray.png" alt="user gray "/><em>user_gray.png</em></td>

+

+		<td><img src="icons/user_green.png" title="user_green.png" alt="user green "/><em>user_green.png</em></td>

+		<td><img src="icons/user_orange.png" title="user_orange.png" alt="user orange "/><em>user_orange.png</em></td>

+		<td><img src="icons/user_red.png" title="user_red.png" alt="user red "/><em>user_red.png</em></td>

+		<td><img src="icons/user_suit.png" title="user_suit.png" alt="user suit "/><em>user_suit.png</em></td>

+		<td><img src="icons/vcard.png" title="vcard.png" alt="vcard "/><em>vcard.png</em></td>

+		<td><img src="icons/vcard_add.png" title="vcard_add.png" alt="vcard add "/><em>vcard_add.png</em></td>

+

+		<td><img src="icons/vcard_delete.png" title="vcard_delete.png" alt="vcard delete "/><em>vcard_delete.png</em></td>

+		<td><img src="icons/vcard_edit.png" title="vcard_edit.png" alt="vcard edit "/><em>vcard_edit.png</em></td>

+		<td><img src="icons/vector.png" title="vector.png" alt="vector "/><em>vector.png</em></td>

+		<td><img src="icons/vector_add.png" title="vector_add.png" alt="vector add "/><em>vector_add.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/vector_delete.png" title="vector_delete.png" alt="vector delete "/><em>vector_delete.png</em></td>

+

+		<td><img src="icons/wand.png" title="wand.png" alt="wand "/><em>wand.png</em></td>

+		<td><img src="icons/weather_clouds.png" title="weather_clouds.png" alt="weather clouds "/><em>weather_clouds.png</em></td>

+		<td><img src="icons/weather_cloudy.png" title="weather_cloudy.png" alt="weather cloudy "/><em>weather_cloudy.png</em></td>

+		<td><img src="icons/weather_lightning.png" title="weather_lightning.png" alt="weather lightning "/><em>weather_lightning.png</em></td>

+		<td><img src="icons/weather_rain.png" title="weather_rain.png" alt="weather rain "/><em>weather_rain.png</em></td>

+		<td><img src="icons/weather_snow.png" title="weather_snow.png" alt="weather snow "/><em>weather_snow.png</em></td>

+

+		<td><img src="icons/weather_sun.png" title="weather_sun.png" alt="weather sun "/><em>weather_sun.png</em></td>

+		<td><img src="icons/webcam.png" title="webcam.png" alt="webcam "/><em>webcam.png</em></td>

+		<td><img src="icons/webcam_add.png" title="webcam_add.png" alt="webcam add "/><em>webcam_add.png</em></td>

+		<td><img src="icons/webcam_delete.png" title="webcam_delete.png" alt="webcam delete "/><em>webcam_delete.png</em></td>

+		<td><img src="icons/webcam_error.png" title="webcam_error.png" alt="webcam error "/><em>webcam_error.png</em></td>

+	</tr>

+

+	<tr>

+		<td><img src="icons/world.png" title="world.png" alt="world "/><em>world.png</em></td>

+		<td><img src="icons/world_add.png" title="world_add.png" alt="world add "/><em>world_add.png</em></td>

+		<td><img src="icons/world_delete.png" title="world_delete.png" alt="world delete "/><em>world_delete.png</em></td>

+		<td><img src="icons/world_edit.png" title="world_edit.png" alt="world edit "/><em>world_edit.png</em></td>

+		<td><img src="icons/world_go.png" title="world_go.png" alt="world go "/><em>world_go.png</em></td>

+

+		<td><img src="icons/world_link.png" title="world_link.png" alt="world link "/><em>world_link.png</em></td>

+		<td><img src="icons/wrench.png" title="wrench.png" alt="wrench "/><em>wrench.png</em></td>

+		<td><img src="icons/wrench_orange.png" title="wrench_orange.png" alt="wrench orange "/><em>wrench_orange.png</em></td>

+		<td><img src="icons/xhtml.png" title="xhtml.png" alt="xhtml "/><em>xhtml.png</em></td>

+		<td><img src="icons/xhtml_add.png" title="xhtml_add.png" alt="xhtml add "/><em>xhtml_add.png</em></td>

+		<td><img src="icons/xhtml_delete.png" title="xhtml_delete.png" alt="xhtml delete "/><em>xhtml_delete.png</em></td>

+

+		<td><img src="icons/xhtml_go.png" title="xhtml_go.png" alt="xhtml go "/><em>xhtml_go.png</em></td>

+	</tr>

+	<tr>

+		<td><img src="icons/xhtml_valid.png" title="xhtml_valid.png" alt="xhtml valid "/><em>xhtml_valid.png</em></td>

+		<td><img src="icons/zoom.png" title="zoom.png" alt="zoom "/><em>zoom.png</em></td>

+		<td><img src="icons/zoom_in.png" title="zoom_in.png" alt="zoom in "/><em>zoom_in.png</em></td>

+		<td><img src="icons/zoom_out.png" title="zoom_out.png" alt="zoom out "/><em>zoom_out.png</em></td>

+

+		<td>&nbsp;</td>

+		<td>&nbsp;</td>

+		<td>&nbsp;</td>

+		<td>&nbsp;</td>

+		<td>&nbsp;</td>

+		<td>&nbsp;</td>

+		<td>&nbsp;</td>

+		<td>&nbsp;</td>

+	</tr></table>

+						</p>

+							

+					</div><!-- /content_inner -->

+					<!-- A break! -->

+					<br class="clear" />

+	 

+			</div><!-- /content_outer -->

+		

+		</div><!-- /container_inner -->

+	</div><!-- /container_outer -->

+

+	

+	</body>

+</html>
\ No newline at end of file
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/readme.txt b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/readme.txt
new file mode 100644
index 0000000..400a64d
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/readme.txt
@@ -0,0 +1,22 @@
+Silk icon set 1.3

+

+_________________________________________

+Mark James

+http://www.famfamfam.com/lab/icons/silk/

+_________________________________________

+

+This work is licensed under a

+Creative Commons Attribution 2.5 License.

+[ http://creativecommons.org/licenses/by/2.5/ ]

+

+This means you may use it for any purpose,

+and make any changes you like.

+All I ask is that you include a link back

+to this page in your credits.

+

+Are you using this icon set? Send me an email

+(including a link or picture if available) to

+mjames@gmail.com

+

+Any other questions about this icon set please

+contact mjames@gmail.com
\ No newline at end of file
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/shape_move_forwards.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/shape_move_forwards.png
new file mode 100644
index 0000000..cfe4493
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/shape_move_forwards.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/shape_move_front.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/shape_move_front.png
new file mode 100644
index 0000000..b4a4e3b
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/shape_move_front.png
Binary files differ
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/shape_square.png b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/shape_square.png
new file mode 100644
index 0000000..33af046
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/famfamfam_icons/shape_square.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..7f7251f
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/environments/environment.prod.ts
@@ -0,0 +1,17 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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..7cd4aaf
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/environments/environment.ts
@@ -0,0 +1,23 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+// 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/src/error.jsp b/org.eclipse.mdm.application/src/main/webapp/src/error.jsp
new file mode 100644
index 0000000..b2b7f79
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/error.jsp
@@ -0,0 +1,50 @@
+<!--********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************-->
+
+<!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>Login failed</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">
+
+				<h2 class="form-signin-heading">Login Error</h2>
+				<h2>Invalid user name or password.</h2>
+
+				<p>
+					Please enter a user name or password that is authorized to access
+					this application. Click here to <a href="${pageContext.request.contextPath}/index.html">Try Again</a>.
+				</p>
+
+			</div>
+		</div>
+	</div>
+
+</body>
+</html>
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..1486d56
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/index.html
@@ -0,0 +1,28 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<!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..0fdc05e
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/login.css
@@ -0,0 +1,60 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..6c35c06
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/login.jsp
@@ -0,0 +1,53 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<!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..c8f7745
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/main.ts
@@ -0,0 +1,25 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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..ebe2b73
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/polyfills.ts
@@ -0,0 +1,55 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+// 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..f3c5e53
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/styles.css
@@ -0,0 +1,107 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+/* 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/famfamfam_icons/database.png");
+}
+.project {
+  background-image: url("assets/famfamfam_icons/house.png");
+}
+.pool {
+  background-image: url("assets/famfamfam_icons/paste_plain.png");
+}
+.test {
+  background-image: url("assets/famfamfam_icons/brick_add.png");
+}
+.teststep {
+  background-image: url("assets/famfamfam_icons/brick.png");
+}
+.measurement {
+  background-image: url("assets/famfamfam_icons/chart_curve.png");
+}
+.channelgroup {
+  background-image: url("assets/famfamfam_icons/calendar.png");
+}
+.channel {
+  background-image: url("assets/famfamfam_icons/chart_curve_go.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..a37b612
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webapp/src/test.ts
@@ -0,0 +1,48 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+// 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/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/webconfig/glassfish-web.xml b/org.eclipse.mdm.application/src/main/webconfig/glassfish-web.xml
new file mode 100644
index 0000000..9726dca
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webconfig/glassfish-web.xml
@@ -0,0 +1,26 @@
+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+<!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>
+    <context-root>/org.eclipse.mdm.nucleus</context-root>
+    <security-role-mapping>
+        <role-name>MDM</role-name>
+        <group-name>MDM</group-name>
+    </security-role-mapping>
+
+</glassfish-web-app>
diff --git a/org.eclipse.mdm.application/src/main/webconfig/web.xml b/org.eclipse.mdm.application/src/main/webconfig/web.xml
new file mode 100644
index 0000000..1adaa50
--- /dev/null
+++ b/org.eclipse.mdm.application/src/main/webconfig/web.xml
@@ -0,0 +1,64 @@
+<!--********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0">
+
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>MDM WEB (protected)</web-resource-name>
+			<url-pattern>/*</url-pattern>
+		</web-resource-collection>
+		<auth-constraint>
+			<role-name>MDM</role-name>
+		</auth-constraint>
+	</security-constraint>
+
+
+	<security-constraint>
+		<web-resource-collection>
+			<web-resource-name>MDM WEB (unprotected)</web-resource-name>
+			<url-pattern>/login.css</url-pattern>
+		</web-resource-collection>
+	</security-constraint>
+
+	<filter>
+		<filter-name>MDMRequestFilter</filter-name>
+		<filter-class>org.eclipse.mdm.application.MDMRequestFilter
+		</filter-class>
+	</filter>
+
+	<filter-mapping>
+		<filter-name>MDMRequestFilter</filter-name>
+		<url-pattern>/*</url-pattern>
+		<dispatcher>REQUEST</dispatcher>
+	</filter-mapping>
+
+	<security-role>
+		<role-name>MDM</role-name>
+	</security-role>
+
+	<login-config>
+		<auth-method>FORM</auth-method>
+		<realm-name>MDMRealm</realm-name>
+		<form-login-config>
+			<form-login-page>/login.jsp</form-login-page>
+			<form-error-page>/error.jsp</form-error-page>
+		</form-login-config>
+	</login-config>
+
+	<session-config>
+		<session-timeout>20</session-timeout>
+	</session-config>
+
+</web-app>
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/build.gradle b/org.eclipse.mdm.businessobjects/build.gradle
new file mode 100644
index 0000000..5b65adc
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/build.gradle
@@ -0,0 +1,43 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+description = 'business object service'
+
+apply plugin: 'antlr'
+
+dependencies {
+	compile project(':org.eclipse.mdm.connector')
+	compileOnly 'com.fasterxml.jackson.core:jackson-databind:2.5.1'
+	compile 'io.vavr:vavr:0.9.1'
+	testCompile 'org.assertj:assertj-core:3.6.2'
+	testCompile 'io.rest-assured:rest-assured:3.0.5'
+	testCompile 'org.glassfish.jersey.test-framework.providers:jersey-test-framework-provider-grizzly2:2.23.2'
+	testCompile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.23.2'
+
+	antlr "org.antlr:antlr4:4.5.3"
+}
+
+generateGrammarSource { arguments += ["-visitor"] }
+
+test {
+    exclude 'org/eclipse/mdm/businessobjects/boundary/integrationtest/*'
+}
+
+compileJava.dependsOn(generateGrammarSource)
+
+jar {
+	metaInf { from '../NOTICE.txt' }
+	metaInf { from '../LICENSE.txt' }
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/antlr/org/eclipse/mdm/businessobjects/filter/FilterGrammar.g4 b/org.eclipse.mdm.businessobjects/src/main/antlr/org/eclipse/mdm/businessobjects/filter/FilterGrammar.g4
new file mode 100644
index 0000000..fcab680
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/antlr/org/eclipse/mdm/businessobjects/filter/FilterGrammar.g4
@@ -0,0 +1,156 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+grammar FilterGrammar;
+
+@header {
+package org.eclipse.mdm.businessobjects.filter;
+}
+
+parse
+ : expression EOF
+ ;
+
+expression
+ : LPAREN expression RPAREN                       #parenExpression
+ | NOT expression                                 #notExpression
+ | left=attribute op=unary_comparator             #unaryComparatorExpression
+ | left=attribute op=comparator right=value       #comparatorExpression
+ | left=attribute op=list_comparator right=values #listComparatorExpression
+ | left=expression op=and right=expression        #andExpression
+ | left=expression op=or right=expression         #orExpression
+ ;
+
+attribute
+ : ATTRIBUTE_IDENTIFIER
+;
+value
+ : STRINGLITERAL 
+ | DECIMAL
+ | LONG
+ | BOOL
+;
+
+values
+ : LPAREN value ( ',' value ) * RPAREN
+;
+
+comparator
+ : EQUAL 
+ | NOT_EQUAL 
+ | LESS_THAN 
+ | LESS_THAN_OR_EQUAL 
+ | GREATER_THAN 
+ | GREATER_THAN_OR_EQUAL 
+ | LIKE
+ | NOT_LIKE
+ | CASE_INSENSITIVE_EQUAL
+ | CASE_INSENSITIVE_NOT_EQUAL
+ | CASE_INSENSITIVE_LESS_THAN
+ | CASE_INSENSITIVE_LESS_THAN_OR_EQUAL
+ | CASE_INSENSITIVE_GREATER_THAN
+ | CASE_INSENSITIVE_GREATER_THAN_OR_EQUAL
+ | CASE_INSENSITIVE_LIKE
+ | CASE_INSENSITIVE_NOT_LIKE
+ ;
+ 
+list_comparator
+ : IN_SET
+ | NOT_IN_SET
+ | CASE_INSENSITIVE_IN_SET
+ | CASE_INSENSITIVE_NOT_IN_SET
+ | BETWEEN
+ ;
+ 
+unary_comparator
+ : IS_NULL
+ | IS_NOT_NULL
+ ;
+  
+and: AND ;
+
+or: OR ;
+
+binary: AND | OR ;
+
+AND                  : A N D ;
+OR                   : O R ;
+NOT                  : N O T ;
+
+EQUAL                                  : E Q ;
+NOT_EQUAL                              : N E ;
+LESS_THAN                              : L T ;
+LESS_THAN_OR_EQUAL                     : L E ;
+GREATER_THAN                           : G T ;
+GREATER_THAN_OR_EQUAL                  : G E ;
+IN_SET                                 : I N ; 
+NOT_IN_SET                             : N O T '_' I N;
+LIKE                                   : L K ;
+NOT_LIKE                               : N O T '_' L K;
+CASE_INSENSITIVE_EQUAL                 : C I '_' E Q;
+CASE_INSENSITIVE_NOT_EQUAL             : C I '_' N E;
+CASE_INSENSITIVE_LESS_THAN             : C I '_' L T;
+CASE_INSENSITIVE_LESS_THAN_OR_EQUAL    : C I '_' L E;
+CASE_INSENSITIVE_GREATER_THAN          : C I '_' G T;
+CASE_INSENSITIVE_GREATER_THAN_OR_EQUAL : C I '_' G E;
+CASE_INSENSITIVE_IN_SET                : C I '_' I N;
+CASE_INSENSITIVE_NOT_IN_SET            : C I '_' N O T '_' I N ;
+CASE_INSENSITIVE_LIKE                  : C I '_' L K;
+CASE_INSENSITIVE_NOT_LIKE              : C I '_' N O T '_' L K ;
+IS_NULL                                : I S '_' N U L L ;
+IS_NOT_NULL                            : I S '_' N O T '_' N U L L;
+BETWEEN                                : B W ;
+
+LPAREN               : '(' ;
+RPAREN               : ')' ;
+DECIMAL              : '-'? DIGIT+ '.' DIGIT+ ;
+LONG                 : '-'? DIGIT+ ;
+BOOL                 : T R U E | F A L S E ;
+IDENTIFIER           : [a-zA-Z_] [a-zA-Z_0-9]* ;
+ATTRIBUTE_IDENTIFIER : IDENTIFIER '.' IDENTIFIER ;
+STRINGLITERAL        : QUOTE ( ~'\'' | ESCAPED_QUOTE )* QUOTE ;
+WS                   : [ \r\t\u000C\n]+ -> skip ;
+
+fragment DIGIT : [0-9] ;
+fragment ESCAPED_QUOTE : '\\\'' ;
+fragment QUOTE         : '\'' ;
+
+/* case insensitive lexer matching */
+fragment A          : ('a'|'A');
+fragment B          : ('b'|'B');
+fragment C          : ('c'|'C');
+fragment D          : ('d'|'D');
+fragment E          : ('e'|'E');
+fragment F          : ('f'|'F');
+fragment G          : ('g'|'G');
+fragment H          : ('h'|'H');
+fragment I          : ('i'|'I');
+fragment J          : ('j'|'J');
+fragment K          : ('k'|'K');
+fragment L          : ('l'|'L');
+fragment M          : ('m'|'M');
+fragment N          : ('n'|'N');
+fragment O          : ('o'|'O');
+fragment P          : ('p'|'P');
+fragment Q          : ('q'|'Q');
+fragment R          : ('r'|'R');
+fragment S          : ('s'|'S');
+fragment T          : ('t'|'T');
+fragment U          : ('u'|'U');
+fragment V          : ('v'|'V');
+fragment W          : ('w'|'W');
+fragment X          : ('x'|'X');
+fragment Y          : ('y'|'Y');
+fragment Z          : ('z'|'Z');
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogAttributeResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogAttributeResource.java
new file mode 100644
index 0000000..ccf25d7
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogAttributeResource.java
@@ -0,0 +1,233 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_DATATYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.CatalogAttribute;
+import org.eclipse.mdm.api.dflt.model.CatalogComponent;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+
+/**
+ * {@link CatalogAttribute} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/catcomps/{" + REQUESTPARAM_CONTEXTTYPE + "}/{" + REQUESTPARAM_ID
+		+ "}/catattrs")
+public class CatalogAttributeResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link CatalogAttribute}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link CatalogAttribute} to load
+	 * @param id
+	 *            id of the {@link CatalogAttribute}
+	 * @return the found {@link CatalogAttribute} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String catCompId,
+			@PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService
+				.find(V(sourceName), CatalogAttribute.class, V(id),
+						ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(catCompId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link CatalogAttribute}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextTypeParam
+	 *            {@link ContextType} of the {@link CatalogAttribute} to load
+	 * @param filter
+	 *            filter string to filter the {@link CatalogAttribute} result
+	 * @return the (filtered) {@link CatalogAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String catCompId,
+			@QueryParam("filter") String filter) {
+		return entityService
+				.find(V(sourceName), CatalogComponent.class, V(catCompId),
+						ServiceUtils.getContextTypeSupplier(contextTypeParam))
+				.map(catComp -> List.ofAll(catComp.getCatalogAttributes()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link CatalogAttributeValue}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextTypeParam
+	 *            {@link ContextType} of the {@link CatalogAttribute} to load
+	 * @param catCompId
+	 *            the identifier of the {@link CatalogComponent} to create an attribute for.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 *            The {@link CatalogAttribute} to create.
+	 * @return the created {@link CatalogAttribute} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String catCompId,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), CatalogAttribute.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+								entityService.getEnumerationValueSupplier(
+										requestBody.getStringValueSupplier(ENTITYATTRIBUTE_DATATYPE)),
+								entityService.find(V(sourceName), CatalogComponent.class, V(catCompId),
+										ServiceUtils.getContextTypeSupplier(contextTypeParam))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link CatalogAttribute} with all parameters set in the given
+	 * JSON body of the request
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextTypeParam
+	 *            {@link ContextType} of the {@link CatalogAttribute} to load
+	 * @param id
+	 *            the identifier of the {@link CatalogAttribute} to update.
+	 * @param catCompId
+	 *            the identifier of the parent {@link CatalogComponent}. 
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link CatalogAttribute}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID2) String id,
+			@PathParam(REQUESTPARAM_ID) String catCompId, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), CatalogAttribute.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(catCompId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link CatalogAttribute}.
+	 * 
+	 * @param id
+	 *            The identifier of the {@link CatalogAttribute} to delete.
+	 * @return the deleted {@link CatalogAttribute }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String catCompId,
+			@PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), CatalogAttribute.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(catCompId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link CatalogAttribute} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), CatalogAttribute.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), CatalogAttribute.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogComponentResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogComponentResource.java
new file mode 100644
index 0000000..3830b0f
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogComponentResource.java
@@ -0,0 +1,206 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.CatalogComponent;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+/**
+ * {@link CatalogComponent} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/catcomps/{" + REQUESTPARAM_CONTEXTTYPE + "}")
+public class CatalogComponentResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link CatalogComponent}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link CatalogComponent} to load
+	 * @param id
+	 *            id of the {@link CatalogComponent}
+	 * @return the found {@link CatalogComponent} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService
+				.find(V(sourceName), CatalogComponent.class, V(id), ServiceUtils.getContextTypeSupplier(contextTypeParam))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link CatalogComponent}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link CatalogComponent} to load
+	 * @param filter
+	 *            filter string to filter the {@link CatalogComponent} result
+	 * @return the (filtered) {@link CatalogComponent}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @QueryParam("filter") String filter) {
+		return entityService
+				.findAll(V(sourceName), CatalogComponent.class, filter,
+						ServiceUtils.getContextTypeSupplier(contextTypeParam))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link CatalogComponentValue}.
+	 * 
+	 * @param body
+	 *            The {@link CatalogComponent} to create.
+	 * @return the created {@link CatalogComponent} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), CatalogComponent.class,
+						L(ServiceUtils.getContextTypeSupplier(contextTypeParam),
+								requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link CatalogComponent} with all parameters set in the given
+	 * JSON body of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link CatalogComponent} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link CatalogComponent}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), CatalogComponent.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link CatalogComponent}.
+	 * 
+	 * @param id
+	 *            The identifier of the {@link CatalogComponent} to delete.
+	 * @return the deleted {@link CatalogComponent }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), CatalogComponent.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link CatalogComponent} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), CatalogComponent.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), CatalogComponent.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogSensorAttributeResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogSensorAttributeResource.java
new file mode 100644
index 0000000..3adb236
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogSensorAttributeResource.java
@@ -0,0 +1,236 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_DATATYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID3;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.CatalogAttribute;
+import org.eclipse.mdm.api.dflt.model.CatalogComponent;
+import org.eclipse.mdm.api.dflt.model.CatalogSensor;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+
+/**
+ * {@link CatalogAttribute} resource handling REST requests
+ * 
+ * @author Philipp Schweinbenz, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/catcomps/testequipment/{" + REQUESTPARAM_ID + "}/catsensors/{"
+		+ REQUESTPARAM_ID2
+		+ "}/catsensorattrs")
+public class CatalogSensorAttributeResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link CatalogAttribute}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param sensorId
+	 *            id of the {@link CatalogSensor}
+	 * @param id
+	 *            id of the {@link CatalogAttribute}
+	 * @return the found {@link CatalogAttribute} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId, @PathParam(REQUESTPARAM_ID2) String sensorId,
+			@PathParam(REQUESTPARAM_ID3) String id) {
+		return entityService
+				.find(V(sourceName), CatalogAttribute.class, V(id),
+						V(ContextType.TESTEQUIPMENT), SL(catCompId, sensorId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER).getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link CatalogAttribute}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param sensorId
+	 *            id of the {@link CatalogSensor}
+	 * @param filter
+	 *            filter string to filter the {@link CatalogAttribute} result
+	 * @return the (filtered) {@link CatalogAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId, @PathParam(REQUESTPARAM_ID2) String sensorId,
+			@QueryParam("filter") String filter) {
+		return entityService
+				.find(V(sourceName), CatalogSensor.class, V(sensorId), V(ContextType.TESTEQUIPMENT), SL(catCompId))
+				.map(catSensor -> List.ofAll(catSensor.getCatalogAttributes()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER).getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link CatalogAttributeValue}.
+	 * 
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param sensorId
+	 *            id of the {@link CatalogSensor}
+	 * @param body
+	 *            The {@link CatalogAttribute} to create.
+	 * @return the created {@link CatalogAttribute} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId,
+			@PathParam(REQUESTPARAM_ID2) String sensorId, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), CatalogAttribute.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+								entityService.getEnumerationValueSupplier(
+										requestBody.getStringValueSupplier(ENTITYATTRIBUTE_DATATYPE)),
+								entityService.find(V(sourceName), CatalogSensor.class, V(sensorId),
+										V(ContextType.TESTEQUIPMENT), SL(catCompId))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER).getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link CatalogAttribute} with all parameters set in the given
+	 * JSON body of the request
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param sensorId
+	 *            id of the {@link CatalogSensor}
+	 * @param id
+	 *            the identifier of the {@link CatalogAttribute} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link CatalogAttribute}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId, @PathParam(REQUESTPARAM_ID2) String sensorId,
+			@PathParam(REQUESTPARAM_ID3) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), CatalogAttribute.class, V(id),
+								V(ContextType.TESTEQUIPMENT), SL(catCompId, sensorId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link CatalogAttribute}.
+	 * 
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param sensorId
+	 *            id of the {@link CatalogSensor}
+	 * @param id
+	 *            The identifier of the {@link CatalogAttribute} to delete.
+	 * @return the deleted {@link CatalogAttribute }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId, @PathParam(REQUESTPARAM_ID2) String sensorId,
+			@PathParam(REQUESTPARAM_ID3) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), CatalogAttribute.class, V(id),
+								V(ContextType.TESTEQUIPMENT), SL(catCompId, sensorId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)).recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link CatalogAttribute} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), CatalogAttribute.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), CatalogAttribute.class, entityService);
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogSensorResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogSensorResource.java
new file mode 100644
index 0000000..ac1fc35
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/CatalogSensorResource.java
@@ -0,0 +1,224 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.CatalogComponent;
+import org.eclipse.mdm.api.dflt.model.CatalogSensor;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+
+/**
+ * {@link CatalogSensor} resource handling REST requests
+ * 
+ * @author Philipp Schweinbenz, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/catcomps/testequipment/{" + REQUESTPARAM_ID
+		+ "}/catsensors")
+public class CatalogSensorResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link CatalogSensor}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param id
+	 *            id of the {@link CatalogSensor}
+	 * @return the found {@link CatalogSensor} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId, @PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService
+				.find(V(sourceName), CatalogSensor.class, V(id), V(ContextType.TESTEQUIPMENT),
+						SL(catCompId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link CatalogSensor}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param filter
+	 *            filter string to filter the {@link CatalogSensor} result
+	 * @return the (filtered) {@link CatalogSensor}s as {@link Response}
+	 */
+	// FIXME anehmer on 2017-11-17: returns all CatalogSensors and not only the ones
+	// in the superordinate CatalogComponent
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId, @QueryParam("filter") String filter) {
+		return entityService.find(V(sourceName), CatalogComponent.class, V(catCompId), V(ContextType.TESTEQUIPMENT))
+				.map(catComp -> List.ofAll(catComp.getCatalogSensors()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link CatalogSensor}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param body
+	 *            The {@link CatalogSensor} to create.
+	 * @return the created {@link CatalogSensor} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), CatalogSensor.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+								entityService.find(V(sourceName), CatalogComponent.class, V(catCompId),
+										V(ContextType.TESTEQUIPMENT))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link CatalogSensor} with all parameters set in the given JSON
+	 * body of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param id
+	 *            the identifier of the {@link CatalogSensor} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link CatalogSensor}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId, @PathParam(REQUESTPARAM_ID2) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), CatalogSensor.class, V(id), V(ContextType.TESTEQUIPMENT),
+								SL(catCompId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link CatalogSensor}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param catCompId
+	 *            id of the {@link CatalogComponent}
+	 * @param id
+	 *            The identifier of the {@link CatalogSensor} to delete.
+	 * @return the deleted {@link CatalogSensor }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String catCompId, @PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), CatalogSensor.class, V(id), V(ContextType.TESTEQUIPMENT),
+								SL(catCompId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link CatalogSensor} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), CatalogSensor.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), CatalogSensor.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelGroupResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelGroupResource.java
new file mode 100644
index 0000000..43b8d3f
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelGroupResource.java
@@ -0,0 +1,125 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.ChannelGroup;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.businessobjects.entity.I18NResponse;
+import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link ChannelGroup} resource
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Path("/environments/{SOURCENAME}/channelgroups")
+public class ChannelGroupResource {
+
+	private static final Logger LOG = LoggerFactory.getLogger(ChannelGroupResource.class);
+
+	@EJB
+	private ChannelGroupService channelGroupService;
+
+	/**
+	 * delegates the request to the {@link ChannelGroupService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link ChannelGroup} result
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getChannelGroups(@PathParam("SOURCENAME") String sourceName, @QueryParam("filter") String filter) {
+
+		try {
+			List<ChannelGroup> channelGroups = this.channelGroupService.getChannelGroups(sourceName, filter);
+			return ServiceUtils.toResponse(new MDMEntityResponse(ChannelGroup.class, channelGroups), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * returns a {@link ChannelGroup} identified by the given id.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param channelId
+	 *            id of the {@link ChannelGroup}
+	 * @return the matching {@link ChannelGroup}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{CHANNELGROUP_ID}")
+	public Response getChannelGroup(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("CHANNELGROUP_ID") String channelGroupId) {
+
+		try {
+			ChannelGroup channelGroup = this.channelGroupService.getChannelGroup(sourceName, channelGroupId);
+			return ServiceUtils.toResponse(new MDMEntityResponse(ChannelGroup.class, channelGroup), 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 ChannelGroupService}
+	 * 
+	 * @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.channelGroupService.localizeAttributes(sourceName);
+			Map<EntityType, String> localizedEntityTypeMap = this.channelGroupService.localizeType(sourceName);
+			return ServiceUtils.toResponse(new I18NResponse(localizedEntityTypeMap, localizedAttributeMap), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), 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/ChannelGroupService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelGroupService.java
new file mode 100644
index 0000000..8b73d4d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelGroupService.java
@@ -0,0 +1,133 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.ChannelGroup;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+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.utils.ServiceUtils;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+
+/**
+ * ChannelGroupService Bean implementation with available {@link ChannelGroup}
+ * operations
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class ChannelGroupService {
+
+	@Inject
+	private ConnectorService connectorService;
+
+	@EJB
+	private I18NActivity I18NActivity;
+	@EJB
+	private NavigationActivity navigationActivity;
+	@EJB
+	private SearchActivity searchActivity;
+
+	/**
+	 * returns the matching {@link ChannelGroup}s using the given filter or all
+	 * {@link ChannelGroup}s if no filter is available
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the ChannelGroup result
+	 * @return the found {@link ChannelGroup}s
+	 */
+	public List<ChannelGroup> getChannelGroups(String sourceName, String filter) {
+		try {
+
+			ApplicationContext context = this.connectorService.getContextByName(sourceName);
+			EntityManager em = context
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+
+			if (filter == null || filter.trim().length() <= 0) {
+				return em.loadAll(ChannelGroup.class);
+			}
+
+			if (ServiceUtils.isParentFilter(context, filter, ChannelGroup.PARENT_TYPE_MEASUREMENT)) {
+				String id = ServiceUtils.extactIdFromParentFilter(context, filter, ChannelGroup.PARENT_TYPE_MEASUREMENT);
+				return this.navigationActivity.getChannelGroups(sourceName, id);
+			}
+
+			return this.searchActivity.search(context, ChannelGroup.class, filter);
+
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns a {@link ChannelGroup identified by the given id
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param channelGroupId
+	 *            id of the {@link ChannelGroup}
+	 * @return the matching {@link ChannelGroup}
+	 */
+	public ChannelGroup getChannelGroup(String sourceName, String channelGroupId) {
+		try {
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			return em.load(ChannelGroup.class, channelGroupId);
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns localized {@link ChannelGroup} attributes
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link ChannelGroup} attributes
+	 */
+	public Map<Attribute, String> localizeAttributes(String sourceName) {
+		return this.I18NActivity.localizeAttributes(sourceName, ChannelGroup.class);
+	}
+
+	/**
+	 * returns the localized {@link ChannelGroup} type name
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link ChannelGroup} type name
+	 */
+	public Map<EntityType, String> localizeType(String sourceName) {
+		return this.I18NActivity.localizeType(sourceName, ChannelGroup.class);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelResource.java
new file mode 100644
index 0000000..202f168
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelResource.java
@@ -0,0 +1,126 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Channel;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Test;
+import org.eclipse.mdm.businessobjects.entity.I18NResponse;
+import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link Channel} resource
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Path("/environments/{SOURCENAME}/channels")
+public class ChannelResource {
+
+	private static final Logger LOG = LoggerFactory.getLogger(ChannelResource.class);
+
+	@EJB
+	private ChannelService channelService;
+
+	/**
+	 * delegates the request to the {@link ChannelService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link Channel} result
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getChannels(@PathParam("SOURCENAME") String sourceName, @QueryParam("filter") String filter) {
+
+		try {
+			List<Channel> channels = this.channelService.getChannels(sourceName, filter);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Channel.class, channels), 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 ChannelService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testId
+	 *            id of the {@link Test}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{CHANNEL_ID}")
+	public Response getChannel(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("CHANNEL_ID") String channelGroupId) {
+
+		try {
+			Channel channel = this.channelService.getChannel(sourceName, channelGroupId);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Channel.class, channel), 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 ChannelService}
+	 * 
+	 * @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.channelService.localizeAttributes(sourceName);
+			Map<EntityType, String> localizedEntityTypeMap = this.channelService.localizeType(sourceName);
+			return ServiceUtils.toResponse(new I18NResponse(localizedEntityTypeMap, localizedAttributeMap), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), 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/ChannelService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelService.java
new file mode 100644
index 0000000..19452b9
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ChannelService.java
@@ -0,0 +1,130 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Channel;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+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.utils.ServiceUtils;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+
+/**
+ * ChannelService Bean implementation with available {@link Channel} operations
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class ChannelService {
+
+	@Inject
+	private ConnectorService connectorService;
+	@EJB
+	private I18NActivity i18nActivity;
+	@EJB
+	private NavigationActivity navigationActivity;
+	@EJB
+	private SearchActivity searchActivity;
+
+	/**
+	 * returns the matching {@link Channel}s using the given filter or all
+	 * {@link Channel}s if no filter is available
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the Channel result
+	 * @return the found {@link Channel}s
+	 */
+	public List<Channel> getChannels(String sourceName, String filter) {
+		try {
+			ApplicationContext context = this.connectorService.getContextByName(sourceName);
+			EntityManager em = context
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+
+			if (filter == null || filter.trim().length() <= 0) {
+				return em.loadAll(Channel.class);
+			}
+
+			if (ServiceUtils.isParentFilter(context, filter, Channel.PARENT_TYPE_CHANNELGROUP)) {
+				String id = ServiceUtils.extactIdFromParentFilter(context, filter, Channel.PARENT_TYPE_CHANNELGROUP);
+				return this.navigationActivity.getChannels(sourceName, id);
+			}
+
+			return this.searchActivity.search(context, Channel.class, filter);
+
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns a {@link Channel} identified by the given id.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param channelId
+	 *            id of the {@link Channel}
+	 * @return the matching {@link Channel}
+	 */
+	public Channel getChannel(String sourceName, String channelId) {
+		try {
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			return em.load(Channel.class, channelId);
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns localized {@link Channel} attributes
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Channel} attributes
+	 */
+	public Map<Attribute, String> localizeAttributes(String sourceName) {
+		return this.i18nActivity.localizeAttributes(sourceName, Channel.class);
+	}
+
+	/**
+	 * returns the localized {@link Channel} type name
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Channel} type name
+	 */
+	public Map<EntityType, String> localizeType(String sourceName) {
+		return this.i18nActivity.localizeType(sourceName, Channel.class);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/EnvironmentResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/EnvironmentResource.java
new file mode 100644
index 0000000..9c46164
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/EnvironmentResource.java
@@ -0,0 +1,170 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+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.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.businessobjects.entity.I18NResponse;
+import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vavr.control.Try;
+
+/**
+ * {@link Environment} resource
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Path("/environments")
+public class EnvironmentResource {
+
+	private static final Logger LOG = LoggerFactory.getLogger(EnvironmentResource.class);
+
+	@EJB
+	private EnvironmentService environmentService;
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * delegates the request to the {@link EnvironmentService}
+	 *
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getEnvironments() {
+		try {
+			List<Environment> environments = this.environmentService.getEnvironments();
+			return ServiceUtils.toResponse(new MDMEntityResponse(Environment.class, environments), 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 EnvironmentService}
+	 * 
+	 * @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("/{" + REQUESTPARAM_SOURCENAME + "}")
+	public Response getEnvironment(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		try {
+			Environment environment = this.environmentService.getEnvironment(sourceName);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Environment.class, environment), Status.OK);
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * Updates the {@link Environment} with all parameters set in the given JSON
+	 * body of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link Environment}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_SOURCENAME + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName), Try.of(() -> this.environmentService.getEnvironment(sourceName)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * delegates the request to the {@link EnvironmentService}
+	 * 
+	 * @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("/{" + REQUESTPARAM_SOURCENAME + "}/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @QueryParam("all") boolean all) {
+
+		try {
+
+			if (all) {
+				Map<Attribute, String> localizedAttributeMap = this.environmentService
+						.localizeAllAttributes(sourceName);
+				Map<EntityType, String> localizedEntityTypeMap = this.environmentService.localizeAllTypes(sourceName);
+				return ServiceUtils.toResponse(new I18NResponse(localizedEntityTypeMap, localizedAttributeMap),
+						Status.OK);
+			}
+
+			Map<Attribute, String> localizedAttributeMap = this.environmentService.localizeAttributes(sourceName);
+			Map<EntityType, String> localizedEntityTypeMap = this.environmentService.localizeType(sourceName);
+			return ServiceUtils.toResponse(new I18NResponse(localizedEntityTypeMap, localizedAttributeMap), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_SOURCENAME + "}/search")
+	public Response search(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @QueryParam("q") String query) {
+		List<Entity> searchResults = environmentService.search(sourceName, query);
+		return ServiceUtils.toResponse(new MDMEntityResponse(Environment.class, searchResults), Status.OK);
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/EnvironmentService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/EnvironmentService.java
new file mode 100644
index 0000000..f37d59e
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/EnvironmentService.java
@@ -0,0 +1,144 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+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.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+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;
+
+/**
+ * {@link Environment} Bean implementation with available {@link Environment}
+ * operations
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class EnvironmentService {
+
+	@Inject
+	private ConnectorService connectorService;
+	@EJB
+	private I18NActivity i18nActivity;
+	@EJB
+	private SearchActivity searchActivity;
+
+	/**
+	 * returns all {@link Environment}s of the connected {@link EntityManager}s
+	 * 
+	 * @return the connected {@link Environment}s
+	 */
+	public List<Environment> getEnvironments() {
+		try {
+			return this.connectorService.getContexts()
+					.stream()
+					.map(ApplicationContext::getEntityManager)
+					.filter(Optional::isPresent)
+				    .map(Optional::get)
+					.map(em -> em.loadEnvironment())
+					.collect(Collectors.toList());
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+
+	}
+
+	/**
+	 * returns the {@link Environment} identified by the given name
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link Environment} with the given name
+	 */
+	public Environment getEnvironment(String sourceName) {
+		try {
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			return em.loadEnvironment();
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns localized {@link Environment} attributes
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Environment} attributes
+	 */
+	public Map<Attribute, String> localizeAttributes(String sourceName) {
+		return this.i18nActivity.localizeAttributes(sourceName, Environment.class);
+	}
+
+	/**
+	 * returns the localized {@link Environment} type name
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Environment} type name
+	 */
+	public Map<EntityType, String> localizeType(String sourceName) {
+		return this.i18nActivity.localizeType(sourceName, Environment.class);
+	}
+
+	/**
+	 * returns all localized attributes
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized attributes from the given source
+	 */
+	public Map<Attribute, String> localizeAllAttributes(String sourceName) {
+		return this.i18nActivity.localizeAllAttributes(sourceName);
+	}
+
+	/**
+	 * returns all localized types
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized types from the given source
+	 */
+	public Map<EntityType, String> localizeAllTypes(String sourceName) {
+		return this.i18nActivity.localizeAllTypes(sourceName);
+	}
+
+	public List<Entity> search(String sourceName, String query) {
+		ApplicationContext context = this.connectorService.getContextByName(sourceName);
+
+		return this.searchActivity.search(context, query);
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/MeasurementResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/MeasurementResource.java
new file mode 100644
index 0000000..4806117
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/MeasurementResource.java
@@ -0,0 +1,282 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.ContextRoot;
+import org.eclipse.mdm.api.base.model.ContextSensor;
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Measurement;
+import org.eclipse.mdm.businessobjects.entity.ContextResponse;
+import org.eclipse.mdm.businessobjects.entity.ContextSensorResponse;
+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 Measurement} resource
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Path("/environments/{SOURCENAME}/measurements")
+public class MeasurementResource {
+
+	private static final Logger LOG = LoggerFactory.getLogger(MeasurementResource.class);
+
+	@EJB
+	private MeasurementService measurementService;
+
+	/**
+	 * delegates the request to the {@link MeasurementService}
+	 * 
+	 * @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.measurementService.getSearchAttributes(sourceName);
+			return ServiceUtils.toResponse(new SearchAttributeResponse(searchAttributes), 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 MeasurementService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the Measurement result
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getMeasurements(@PathParam("SOURCENAME") String sourceName, @QueryParam("filter") String filter) {
+
+		try {
+			List<Measurement> measurements = this.measurementService.getMeasurements(sourceName, filter);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Measurement.class, measurements), 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 MeasurementService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param MeasurementId
+	 *            id of the {@link Measurement}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{MEASUREMENT_ID}")
+	public Response getMeasurement(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("MEASUREMENT_ID") String measurementId) {
+
+		try {
+			Measurement measurement = this.measurementService.getMeasurement(sourceName, measurementId);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Measurement.class, measurement), 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 MeasurementService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param MeasurementId
+	 *            id of the {@link Measurement}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{MEASUREMENT_ID}/contexts")
+	public Response getContext(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("MEASUREMENT_ID") String measurementId) {
+		try {
+			Map<String, Map<ContextType, ContextRoot>> contextMap = this.measurementService.getContext(sourceName,
+					measurementId);
+			return ServiceUtils.toResponse(new ContextResponse(contextMap), 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 MeasurementService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param MeasurementId
+	 *            id of the {@link Measurement}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{MEASUREMENT_ID}/contexts/unitundertest")
+	public Response getContextUUT(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("MEASUREMENT_ID") String measurementId) {
+		try {
+			Map<String, Map<ContextType, ContextRoot>> contextMap = this.measurementService.getContextUUT(sourceName,
+					measurementId);
+			return ServiceUtils.toResponse(new ContextResponse(contextMap), 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 MeasurementService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param MeasurementId
+	 *            id of the {@link Measurement}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{MEASUREMENT_ID}/contexts/testsequence")
+	public Response getContextTSQ(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("MEASUREMENT_ID") String measurementId) {
+		try {
+			Map<String, Map<ContextType, ContextRoot>> contextMap = this.measurementService.getContextTSQ(sourceName,
+					measurementId);
+			return ServiceUtils.toResponse(new ContextResponse(contextMap), 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 MeasurementService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param MeasurementId
+	 *            id of the {@link Measurement}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{MEASUREMENT_ID}/contexts/testequipment")
+	public Response getContextTEQ(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("MEASUREMENT_ID") String measurementId) {
+		try {
+			Map<String, Map<ContextType, ContextRoot>> contextMap = this.measurementService.getContextTEQ(sourceName,
+					measurementId);
+			return ServiceUtils.toResponse(new ContextResponse(contextMap), 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 MeasurementService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param MeasurementId
+	 *            id of the {@link Measurement}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{MEASUREMENT_ID}/contexts/testequipment/sensors")
+	public Response getContextTEQSensors(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("MEASUREMENT_ID") String measurementId) {
+		try {
+			try {
+				Map<String, List<ContextSensor>> sensorMap = this.measurementService.getSensors(sourceName,
+						measurementId);
+				return ServiceUtils.toResponse(new ContextSensorResponse(sensorMap), Status.OK);
+			} catch (RuntimeException e) {
+				LOG.error(e.getMessage(), e);
+				throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+			}
+
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * delegates the request to the {@link MeasurementService}
+	 * 
+	 * @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.measurementService.localizeAttributes(sourceName);
+			Map<EntityType, String> localizedEntityTypeMap = this.measurementService.localizeType(sourceName);
+			return ServiceUtils.toResponse(new I18NResponse(localizedEntityTypeMap, localizedAttributeMap), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), 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/MeasurementService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/MeasurementService.java
new file mode 100644
index 0000000..d700e61
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/MeasurementService.java
@@ -0,0 +1,220 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.ContextRoot;
+import org.eclipse.mdm.api.base.model.ContextSensor;
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Measurement;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.businessobjects.control.ContextActivity;
+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;
+
+/**
+ * MeasurementService Bean implementation with available {@link Measurement}
+ * operations
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class MeasurementService {
+
+	@Inject
+	private ConnectorService connectorService;
+	@EJB
+	private I18NActivity i18nActivity;
+	@EJB
+	private NavigationActivity navigationActivity;
+	@EJB
+	private ContextActivity contextActivity;
+	@EJB
+	private SearchActivity searchActivity;
+
+	/**
+	 * returns the matching {@link Measurement}s using the given filter or all
+	 * {@link Measurement}s if no filter is available
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link Measurement} result
+	 * @return the found {@link Measurement}s
+	 */
+	public List<Measurement> getMeasurements(String sourceName, String filter) {
+		try {
+			ApplicationContext context = this.connectorService.getContextByName(sourceName);
+			EntityManager em = context
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+
+			if (filter == null || filter.trim().length() <= 0) {
+				return em.loadAll(Measurement.class);
+			}
+
+			if (ServiceUtils.isParentFilter(context, filter, Measurement.PARENT_TYPE_TESTSTEP)) {
+				String id = ServiceUtils.extactIdFromParentFilter(context, filter, Measurement.PARENT_TYPE_TESTSTEP);
+				return this.navigationActivity.getMeasurements(sourceName, id);
+			}
+
+			return this.searchActivity.search(context, Measurement.class, filter);
+
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * Returns the {@link SearchAttribute} for the entity type Measurement 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) {
+		return this.searchActivity.listAvailableAttributes(this.connectorService.getContextByName(sourceName), Measurement.class);
+	}
+
+	/**
+	 * returns a {@link Measurement} identified by the given id.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param measurementId
+	 *            id of the {@link Measurement}
+	 * @return the matching {@link Measurement}
+	 */
+	public Measurement getMeasurement(String sourceName, String measurementId) {
+		try {
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			return em.load(Measurement.class, measurementId);
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns the complete context data (ordered and measured) for a
+	 * {@link Measurement}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param measurementId
+	 *            id of the {@link Measurement}
+	 * @return a map with the complete context data (ordered and measured)
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getContext(String sourceName, String measurementId) {
+		return this.contextActivity.getMeasurementContext(sourceName, measurementId);
+	}
+
+	/**
+	 * returns the UnitUnderTest context data (ordered and measured) for a
+	 * {@link Measurement}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param measurementIdId
+	 *            id of the {@link Measurement}
+	 * @return a map with the UnitUnderTest context data (ordered and measured)
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getContextUUT(String sourceName, String measurementId) {
+		return this.contextActivity.getMeasurementContext(sourceName, measurementId, ContextType.UNITUNDERTEST);
+	}
+
+	/**
+	 * returns the TestSequence context data (ordered and measured) for a
+	 * {@link Measurement}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param measurementId
+	 *            id of the {@link Measurement}
+	 * @return a map with the TestSequence context data (ordered and measured)
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getContextTSQ(String sourceName, String measurementId) {
+		return this.contextActivity.getMeasurementContext(sourceName, measurementId, ContextType.TESTSEQUENCE);
+	}
+
+	/**
+	 * returns the TestEquipment context data (ordered and measured) for a
+	 * {@link Measurement}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param measurementId
+	 *            id of the {@link Measurement}
+	 * @return a map with the TestEquipment context data (ordered and measured)
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getContextTEQ(String sourceName, String measurementId) {
+		return this.contextActivity.getMeasurementContext(sourceName, measurementId, ContextType.TESTEQUIPMENT);
+	}
+
+	/**
+	 * returns all sensor context data of TestEquipment sensor configuration
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param measurementId
+	 *            id of the {@link Measurement}
+	 * @return a map with the TestEquipment sensor context data (ordered and
+	 *         measured)
+	 */
+	public Map<String, List<ContextSensor>> getSensors(String sourceName, String measurementId) {
+		return this.contextActivity.getMeasurementSensorContext(sourceName, measurementId);
+	}
+
+	/**
+	 * returns localized {@link Measurement} attributes
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Measurement} attributes
+	 */
+	public Map<Attribute, String> localizeAttributes(String sourceName) {
+		return this.i18nActivity.localizeAttributes(sourceName, Measurement.class);
+	}
+
+	/**
+	 * returns the localized {@link Measurement} type name
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Measurement} type name
+	 */
+	public Map<EntityType, String> localizeType(String sourceName) {
+		return this.i18nActivity.localizeType(sourceName, Measurement.class);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/NestedTemplateAttributeResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/NestedTemplateAttributeResource.java
new file mode 100644
index 0000000..aefd53f
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/NestedTemplateAttributeResource.java
@@ -0,0 +1,233 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID3;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID4;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.TemplateAttribute;
+import org.eclipse.mdm.api.dflt.model.TemplateComponent;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+
+/**
+ * {@link NestedTemplateAttribute} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tplroots/{" + REQUESTPARAM_CONTEXTTYPE + "}/{" + REQUESTPARAM_ID
+		+ "}/tplcomps/{" + REQUESTPARAM_ID2 + "}/tplcomps/{" + REQUESTPARAM_ID3 + "}/tplattrs")
+public class NestedTemplateAttributeResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found nested {@link TemplateAttribute}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the nested {@link TemplateAttribute} to
+	 *            load
+	 * @param id
+	 *            id of the nested {@link TemplateAttribute}
+	 * @return the found nested {@link TemplateAttribute} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID4 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, @PathParam(REQUESTPARAM_ID3) String tplCompId,
+			@PathParam(REQUESTPARAM_ID4) String id) {
+		return entityService.find(V(sourceName), TemplateAttribute.class, V(id),
+				ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId, parentTplCompId, tplCompId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) nested {@link TemplateAttribute}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the nested {@link TemplateAttribute} to
+	 *            load
+	 * @param filter
+	 *            filter string to filter the nested {@link TemplateAttribute}
+	 *            result
+	 * @return the (filtered) nested {@link TemplateAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, @PathParam(REQUESTPARAM_ID3) String tplCompId,
+			@QueryParam("filter") String filter) {
+		return entityService
+				.find(V(sourceName), TemplateComponent.class, V(tplCompId),
+						ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId, parentTplCompId))
+				.map(tplComp -> List.ofAll(tplComp.getTemplateAttributes()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created nested {@link TemplateAttribute}.
+	 * 
+	 * @param body
+	 *            The nested {@link TemplateAttribute} to create.
+	 * @return The created nested {@link TemplateAttribute} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, @PathParam(REQUESTPARAM_ID3) String tplCompId,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), TemplateAttribute.class, L(
+						requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+						entityService.find(V(sourceName), TemplateComponent.class, V(tplCompId),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId, parentTplCompId))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link TemplateAttribute} with all parameters set in the given
+	 * JSON body of the request
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the nested {@link TemplateAttribute} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated nested {@link TemplateAttribute}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID4 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, @PathParam(REQUESTPARAM_ID3) String tplCompId,
+			@PathParam(REQUESTPARAM_ID4) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), TemplateAttribute.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam),
+								SL(tplRootId, parentTplCompId, tplCompId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link NestedTemplateAttribute}.
+	 * 
+	 * 
+	 * @param id
+	 *            The identifier of the {@link NestedTemplateAttribute} to delete.
+	 * @return the deleted {@link NestedTemplateAttribute }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID4 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, @PathParam(REQUESTPARAM_ID3) String tplCompId,
+			@PathParam(REQUESTPARAM_ID4) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), TemplateAttribute.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam),
+								SL(tplRootId, parentTplCompId, tplCompId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link TemplateAttribute} type.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateAttribute.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateAttribute.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/NestedTemplateComponentResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/NestedTemplateComponentResource.java
new file mode 100644
index 0000000..e7a6c6d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/NestedTemplateComponentResource.java
@@ -0,0 +1,228 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_CATALOGCOMPONENT_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID3;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.CatalogComponent;
+import org.eclipse.mdm.api.dflt.model.TemplateComponent;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+
+/**
+ * {@link TemplateComponent} resource handling REST requests for nested
+ * {@link TemplateComponent}s
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tplroots/{" + REQUESTPARAM_CONTEXTTYPE + "}/{" + REQUESTPARAM_ID
+		+ "}/tplcomps/{" + REQUESTPARAM_ID2 + "}/tplcomps")
+public class NestedTemplateComponentResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found nested {@link TemplateComponent}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateComponent} to load
+	 * @param id
+	 *            id of the {@link TemplateComponent}
+	 * @return the found {@link TemplateComponent} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, @PathParam(REQUESTPARAM_ID3) String id) {
+		return entityService
+				.find(V(sourceName), TemplateComponent.class, V(id), ServiceUtils.getContextTypeSupplier(contextTypeParam),
+						SL(tplRootId, parentTplCompId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) nested {@link TemplateComponent}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateComponent} to load
+	 * @param filter
+	 *            filter string to filter the {@link TemplateComponent} result
+	 * @return the (filtered) {@link TemplateComponent}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, @QueryParam("filter") String filter) {
+		return entityService
+				.find(V(sourceName), TemplateComponent.class, V(parentTplCompId),
+						ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId))
+				.map(tplComp -> List.ofAll(tplComp.getTemplateComponents()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link TemplateComponentValue}.
+	 * 
+	 * 
+	 * @param body
+	 *            The {@link TemplateComponent} to create.
+	 * @param id
+	 *            the identifier of the parent {@link TemplateComponent}.
+	 * @return the created {@link TemplateComponent} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), TemplateComponent.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+						entityService.find(V(sourceName), TemplateComponent.class, V(parentTplCompId),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId)),
+						entityService.find(V(sourceName), CatalogComponent.class,
+								requestBody.getStringValueSupplier(ENTITYATTRIBUTE_CATALOGCOMPONENT_ID),
+										ServiceUtils.getContextTypeSupplier(contextTypeParam))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the nested {@link TemplateComponent} with all parameters set in the
+	 * given JSON body of the request
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link TemplateComponent} to update.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link TemplateComponent}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, @PathParam(REQUESTPARAM_ID3) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), TemplateComponent.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId, parentTplCompId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted nested {@link TemplateComponent}.
+	 * 
+	 * @param id
+	 *            The identifier of the {@link TemplateComponent} to delete.
+	 * @return the deleted {@link TemplateComponent }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String parentTplCompId, @PathParam(REQUESTPARAM_ID3) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), TemplateComponent.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId, parentTplCompId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the nested {@link TemplateComponent} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateComponent.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateComponent.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PhysicalDimensionResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PhysicalDimensionResource.java
new file mode 100755
index 0000000..ebd6881
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PhysicalDimensionResource.java
@@ -0,0 +1,190 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.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.model.PhysicalDimension;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+/**
+ * {@link PhysicalDimension} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/physicaldimensions")
+public class PhysicalDimensionResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link PhysicalDimension}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            id of the {@link PhysicalDimension}
+	 * @return the found {@link PhysicalDimension} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.find(V(sourceName), PhysicalDimension.class, V(id))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link PhysicalDimension}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link PhysicalDimension} result
+	 * @return the (filtered) {@link PhysicalDimension}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@QueryParam("filter") String filter) {
+		return entityService.findAll(V(sourceName), PhysicalDimension.class, filter)
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link PhysicalDimension}.
+	 * 
+	 * @param body
+	 *            The {@link PhysicalDimension} to create.
+	 * @return the created {@link PhysicalDimension} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), PhysicalDimension.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link PhysicalDimension} with all parameters set in the given
+	 * JSON body of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link PhysicalDimension} to update.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link PhysicalDimension}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName), entityService.find(V(sourceName), PhysicalDimension.class, V(id)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link PhysicalDimension}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            The identifier of the {@link PhysicalDimension} to delete.
+	 * @return the deleted {@link PhysicalDimension }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.delete(V(sourceName), entityService.find(V(sourceName), PhysicalDimension.class, V(id)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link PhysicalDimension} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), PhysicalDimension.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), PhysicalDimension.class, entityService);
+	}
+}
\ No newline at end of file
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..e71baec
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolResource.java
@@ -0,0 +1,144 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Environment;
+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") String 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..36895da
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/PoolService.java
@@ -0,0 +1,172 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+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 {
+
+	@Inject
+	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 {
+			ApplicationContext context = this.connectorService.getContextByName(sourceName);
+			EntityManager em = context
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+
+			if (filter == null || filter.trim().length() <= 0) {
+				return em.loadAll(Pool.class);
+			}
+
+			if (ServiceUtils.isParentFilter(context, filter, Pool.PARENT_TYPE_PROJECT)) {
+				String id = ServiceUtils.extactIdFromParentFilter(context, filter, Pool.PARENT_TYPE_PROJECT);
+				return this.navigationActivity.getPools(sourceName, id);
+			}
+
+			return this.searchActivity.search(context, 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) {
+		return this.searchActivity.listAvailableAttributes(this.connectorService.getContextByName(sourceName), 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, String poolId) {
+		try {
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			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..56c5f7d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ProjectResource.java
@@ -0,0 +1,230 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Environment;
+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.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO: Use entityService (and vavr) in all Methods
+
+/**
+ * {@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;
+
+	@EJB
+	private EntityService entityService;
+	
+	/**
+	 * 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") String 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);
+		}
+	}
+
+	/**
+	 * Returns the created {@link Project}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param body
+	 *            The {@link Project} to create.
+	 * @return the created {@link Project} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), Project.class, L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+	
+	/**
+	 * Updates the {@link Project} with all parameters set in the given JSON body of
+	 * the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link ProjectValue} to update.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link Project}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName), entityService.find(V(sourceName), Project.class, V(id)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link Project}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            The identifier of the {@link Project} to delete.
+	 * @return the deleted {@link ValueList }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.delete(V(sourceName), entityService.find(V(sourceName), Project.class, V(id)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+}
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..b2efd11
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ProjectService.java
@@ -0,0 +1,159 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+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 {
+
+	@Inject
+	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 {
+			ApplicationContext context = this.connectorService.getContextByName(sourceName);
+			EntityManager em = context
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+
+			if (filter == null || filter.trim().length() <= 0) {
+				return em.loadAll(Project.class);
+			}
+
+			return this.searchActivity.search(context, 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) {
+		return this.searchActivity.listAvailableAttributes(this.connectorService.getContextByName(sourceName), 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, String projectId) {
+		try {
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			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/QuantityResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/QuantityResource.java
new file mode 100644
index 0000000..98b4237
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/QuantityResource.java
@@ -0,0 +1,198 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_UNIT_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.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.model.Quantity;
+import org.eclipse.mdm.api.base.model.Unit;
+import org.eclipse.mdm.api.dflt.model.ValueListValue;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+/**
+ * {@link Quantity} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/quantities")
+public class QuantityResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link Quantity}. {@link WebApplicationException} on error.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            id of the {@link Quantity}
+	 * @return the found {@link Quantity} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.find(V(sourceName), Quantity.class, V(id))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link Quantity}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link Quantity} result
+	 * @return the (filtered) {@link Quantity}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@QueryParam("filter") String filter) {
+		return entityService.findAll(V(sourceName), Quantity.class, filter)
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link Unit}.
+	 * 
+	 * @param body
+	 *            The {@link Unit} to create.
+	 * @return the created {@link Unit} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), Quantity.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+								entityService.find(V(sourceName), Unit.class,
+										requestBody.getStringValueSupplier(ENTITYATTRIBUTE_UNIT_ID))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link Quantity} with all parameters set in the given JSON body
+	 * of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link Quantity} to update.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link ValueListValue}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName), entityService.find(V(sourceName), Quantity.class, V(id)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link Quantity}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            The identifier of the {@link Quantity} to delete.
+	 * @return the deleted {@link Quantity }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.delete(V(sourceName), entityService.find(V(sourceName), Quantity.class, V(id)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link Quantity} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), Quantity.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), Quantity.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ResourceConstants.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ResourceConstants.java
new file mode 100644
index 0000000..feb9835
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ResourceConstants.java
@@ -0,0 +1,128 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Quantity;
+import org.eclipse.mdm.api.base.model.Unit;
+import org.eclipse.mdm.api.dflt.model.CatalogComponent;
+import org.eclipse.mdm.api.dflt.model.CatalogSensor;
+import org.eclipse.mdm.api.dflt.model.TemplateAttribute;
+import org.eclipse.mdm.api.dflt.model.TemplateComponent;
+import org.eclipse.mdm.api.dflt.model.TemplateRoot;
+import org.eclipse.mdm.api.dflt.model.TemplateSensor;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStep;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStepUsage;
+
+/**
+ * Class defining constants used by the specific Jersey resource classes.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+public final class ResourceConstants {
+	/**
+	 * Parameter name holding the {@link Environment}, i.e. the source name
+	 */
+	public static final String REQUESTPARAM_SOURCENAME = "SOURCENAME";
+
+	/**
+	 * Parameter holding the {@link Entity}s id in the URI path
+	 */
+	public static final String REQUESTPARAM_ID = "ID";
+
+	/**
+	 * Parameter holding an additional {@link Entity}s id in the URI path, e.g. for
+	 * a {@link TemplateComponent} when {@link REQUESTPARAM_ID} holds the id of the
+	 * {@link TemplateRoot}.
+	 */
+	public static final String REQUESTPARAM_ID2 = "ID2";
+
+	/**
+	 * Parameter holding an additional {@link Entity}s id in the URI path, e.g. for
+	 * a {@link TemplateAttribute} when {@link REQUESTPARAM_ID} holds the id of the
+	 * {@link TemplateRoot} and {@link REQUESTPARAM_ID2} holds the id of the
+	 * {@link TemplateComponent}.
+	 */
+	public static final String REQUESTPARAM_ID3 = "ID3";
+
+	/**
+	 * Parameter holding an additional {@link Entity}s id in the URI path, e.g. for
+	 * a {@link TemplateAttribute} when {@link REQUESTPARAM_ID} holds the id of the
+	 * {@link TemplateRoot}, {@link REQUESTPARAM_ID2} holds the id of the parent
+	 * {@link TemplateComponent} and {@link REQUESTPARAM_ID3} holds the id of the
+	 * nested {@link TemplateComponent}.
+	 */
+	public static final String REQUESTPARAM_ID4 = "ID4";
+
+	/**
+	 * Parameter holding the {@link ContextType} of the {@link Entity} in the URI
+	 * path
+	 */
+	public static final String REQUESTPARAM_CONTEXTTYPE = "CONTEXTTYPE";
+
+	/**
+	 * Parameter holding the name of the {@link Entity} in the request body
+	 */
+	public static final String ENTITYATTRIBUTE_NAME = "Name";
+
+	/**
+	 * Parameter holding the {@link ValueType} of the {@link Entity} in the request
+	 * body
+	 */
+	public static final String ENTITYATTRIBUTE_DATATYPE = "DataType";
+
+	/**
+	 * Parameter holding the id of the {@link CatalogComponent} of e.g. the
+	 * {@link TemplateComponent} in the request body
+	 */
+	public static final String ENTITYATTRIBUTE_CATALOGCOMPONENT_ID = "CatalogComponent";
+
+	/**
+	 * Parameter holding the id of the {@link CatalogSensor} of e.g. the
+	 * {@link TemplateSensor} in the request body
+	 */
+	public static final String ENTITYATTRIBUTE_CATALOGSENSOR_ID = "CatalogSensor";
+
+	/**
+	 * Parameter holding the id of the {@link Quantity} of e.g. the {@link Unit} in
+	 * the request body
+	 */
+	public static final String ENTITYATTRIBUTE_QUANTITY_ID = "Quantity";
+
+	/**
+	 * Parameter holding the id of the {@link Unit} of e.g. the {@link Quantity} in
+	 * the request body
+	 */
+	public static final String ENTITYATTRIBUTE_UNIT_ID = "Unit";
+
+	/**
+	 * Parameter holding the id of the {@link Unit} of e.g. the {@link Quantity} in
+	 * the request body
+	 */
+	public static final String ENTITYATTRIBUTE_PHYSICALDIMENSION_ID = "PhysicalDimension";
+
+	/**
+	 * Parameter holding the id of the {@link TemplateTestStep} of e.g. the
+	 * {@link TemplateTestStepUsage} in the request body
+	 */
+	public static final String ENTITYATTRIBUTE_TEMPLATETESTSTEP_ID = "TemplateTestStep";
+
+	/**
+	 * Just hide the default constructor
+	 */
+	private ResourceConstants() {
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateAttributeResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateAttributeResource.java
new file mode 100644
index 0000000..e79767c
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateAttributeResource.java
@@ -0,0 +1,220 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID3;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.TemplateAttribute;
+import org.eclipse.mdm.api.dflt.model.TemplateComponent;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+
+/**
+ * {@link TemplateAttribute} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tplroots/{" + REQUESTPARAM_CONTEXTTYPE + "}/{" + REQUESTPARAM_ID
+		+ "}/tplcomps/{" + REQUESTPARAM_ID2 + "}/tplattrs")
+public class TemplateAttributeResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link TemplateAttribute}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateAttribute} to load
+	 * @param id
+	 *            id of the {@link TemplateAttribute}
+	 * @return the found {@link TemplateAttribute} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String tplCompId, @PathParam(REQUESTPARAM_ID3) String id) {
+		return entityService
+				.find(V(sourceName), TemplateAttribute.class, V(id),
+						ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId, tplCompId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link TemplateAttribute}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateAttribute} to load
+	 * @param filter
+	 *            filter string to filter the {@link TemplateAttribute} result
+	 * @return the (filtered) {@link TemplateAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String tplCompId, @QueryParam("filter") String filter) {
+		return entityService
+				.find(V(sourceName), TemplateComponent.class, V(tplCompId),
+						ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId))
+				.map(tplComp -> List.ofAll(tplComp.getTemplateAttributes()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link TemplateAttributeValue}.
+	 * 
+	 * @param body
+	 *            The {@link TemplateAttribute} to create.
+	 * @return the created {@link TemplateAttribute} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String tplCompId, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), TemplateAttribute.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+								entityService.find(V(sourceName), TemplateComponent.class, V(tplCompId),
+										ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link TemplateAttribute} with all parameters set in the given
+	 * JSON body of the request
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link TemplateAttribute} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link TemplateAttribute}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String tplCompId, @PathParam(REQUESTPARAM_ID3) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), TemplateAttribute.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId, tplCompId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link TemplateAttribute}.
+	 * 
+	 * @param id
+	 *            The identifier of the {@link TemplateAttribute} to delete.
+	 * @return the deleted {@link TemplateAttribute }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String tplCompId, @PathParam(REQUESTPARAM_ID3) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), TemplateAttribute.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId, tplCompId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link TemplateAttribute} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateAttribute.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateAttribute.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateComponentResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateComponentResource.java
new file mode 100644
index 0000000..f396495
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateComponentResource.java
@@ -0,0 +1,224 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_CATALOGCOMPONENT_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.CatalogComponent;
+import org.eclipse.mdm.api.dflt.model.TemplateComponent;
+import org.eclipse.mdm.api.dflt.model.TemplateRoot;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+
+/**
+ * {@link TemplateComponent} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tplroots/{" + REQUESTPARAM_CONTEXTTYPE + "}/{" + REQUESTPARAM_ID
+		+ "}/tplcomps")
+public class TemplateComponentResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link TemplateComponent}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateComponent} to load
+	 * @param id
+	 *            id of the {@link TemplateComponent}
+	 * @return the found {@link TemplateComponent} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService
+				.find(V(sourceName), TemplateComponent.class, V(id), ServiceUtils.getContextTypeSupplier(contextTypeParam),
+						SL(tplRootId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link TemplateComponent}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateComponent} to load
+	 * @param filter
+	 *            filter string to filter the {@link TemplateComponent} result
+	 * @return the (filtered) {@link TemplateComponent}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@QueryParam("filter") String filter) {
+		return entityService
+				.find(V(sourceName), TemplateRoot.class, V(tplRootId),
+						ServiceUtils.getContextTypeSupplier(contextTypeParam))
+				.map(tplRoot -> List.ofAll(tplRoot.getTemplateComponents()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link TemplateComponentValue}.
+	 * 
+	 * @param body
+	 *            The {@link TemplateComponent} to create.
+	 * @return the created {@link TemplateComponent} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), TemplateComponent.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+						entityService.find(V(sourceName), TemplateRoot.class, V(tplRootId),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam)),
+						entityService.find(V(sourceName), CatalogComponent.class,
+								requestBody.getStringValueSupplier(ENTITYATTRIBUTE_CATALOGCOMPONENT_ID),
+										ServiceUtils.getContextTypeSupplier(contextTypeParam))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link TemplateComponent} with all parameters set in the given
+	 * JSON body of the request
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link TemplateComponent} to update.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link TemplateComponent}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), TemplateComponent.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link TemplateComponent}.
+	 * 
+	 * @param id
+	 *            The identifier of the {@link TemplateComponent} to delete.
+	 * @return the deleted {@link TemplateComponent }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String tplRootId,
+			@PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), TemplateComponent.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam), SL(tplRootId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link TemplateComponent} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateComponent.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateComponent.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateRootResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateRootResource.java
new file mode 100644
index 0000000..2f36ccb
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateRootResource.java
@@ -0,0 +1,208 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.TemplateRoot;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+/**
+ * {@link TemplateRoot} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tplroots/{" + REQUESTPARAM_CONTEXTTYPE + "}")
+public class TemplateRootResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link TemplateRoot}.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateRoot} to load
+	 * @param id
+	 *            id of the {@link TemplateRoot}
+	 * @return the found {@link TemplateRoot} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService
+				.find(V(sourceName), TemplateRoot.class, V(id), ServiceUtils.getContextTypeSupplier(contextTypeParam))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link TemplateRoot}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateRoot} to load
+	 * @param filter
+	 *            filter string to filter the {@link TemplateRoot} result
+	 * @return the (filtered) {@link TemplateRoot}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @QueryParam("filter") String filter) {
+		return entityService
+				.findAll(V(sourceName), TemplateRoot.class, filter, ServiceUtils.getContextTypeSupplier(contextTypeParam))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link TemplateRootValue}.
+	 * 
+	 * @param body
+	 *            The {@link TemplateRoot} to create.
+	 * @return the created {@link TemplateRoot} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), TemplateRoot.class,
+						L(ServiceUtils.getContextTypeSupplier(contextTypeParam),
+								requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link TemplateRoot} with all parameters set in the given JSON
+	 * body of the request
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link TemplateRoot} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link TemplateRoot}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), TemplateRoot.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link TemplateRoot}.
+	 * 
+	 * @param id
+	 *            The identifier of the {@link TemplateRoot} to delete.
+	 * @return the deleted {@link TemplateRoot }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), TemplateRoot.class, V(id),
+								ServiceUtils.getContextTypeSupplier(contextTypeParam)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link TemplateRoot} type.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateRoot.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateRoot.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateSensorAttributeResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateSensorAttributeResource.java
new file mode 100644
index 0000000..6b3b2e6
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateSensorAttributeResource.java
@@ -0,0 +1,194 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID3;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID4;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.TemplateAttribute;
+import org.eclipse.mdm.api.dflt.model.TemplateComponent;
+import org.eclipse.mdm.api.dflt.model.TemplateRoot;
+import org.eclipse.mdm.api.dflt.model.TemplateSensor;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+
+/**
+ * {@link TemplateAttribute} resource handling REST requests
+ * 
+ * @author Philipp Schweinbenz, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tplroots/testequipment/{" + REQUESTPARAM_ID + "}/tplcomps/{"
+		+ REQUESTPARAM_ID2 + "}/tplsensors/{" + REQUESTPARAM_ID3 + "}/tplsensorattrs")
+public class TemplateSensorAttributeResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/*
+	 * Create not implemented as TemplateSensorAttributes are created implicitly
+	 * with the TemplateSensor
+	 */
+
+	/**
+	 * Returns the found {@link TemplateAttribute}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param tplRootId
+	 *            id of the {@link TemplateRoot}
+	 * @param tplCompId
+	 *            id of the {@link TemplateComponent}
+	 * @param tplSensorId
+	 *            id of the {@link TemplateSensor}
+	 * @param id
+	 *            id of the {@link TemplateAttribute}
+	 * @return the found {@link TemplateAttribute} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID4 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplRootId, @PathParam(REQUESTPARAM_ID2) String tplCompId,
+			@PathParam(REQUESTPARAM_ID3) String tplSensorId, @PathParam(REQUESTPARAM_ID4) String id) {
+		return entityService
+				.find(V(sourceName), TemplateAttribute.class, V(id), V(ContextType.TESTEQUIPMENT),
+						SL(tplRootId, tplCompId, tplSensorId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link TemplateAttribute}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param tplRootId
+	 *            id of the {@link TemplateRoot}
+	 * @param tplCompId
+	 *            id of the {@link TemplateComponent}
+	 * @param tplSensorId
+	 *            id of the {@link TemplateSensor}
+	 * @param filter
+	 *            filter string to filter the {@link TemplateAttribute} result
+	 * @return the (filtered) {@link TemplateAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplRootId, @PathParam(REQUESTPARAM_ID2) String tplCompId,
+			@PathParam(REQUESTPARAM_ID3) String tplSensorId, @QueryParam("filter") String filter) {
+		return entityService
+				.find(V(sourceName), TemplateSensor.class, V(tplSensorId),
+						V(ContextType.TESTEQUIPMENT), SL(tplRootId, tplCompId))
+				.map(tplSensor -> List.ofAll(tplSensor.getTemplateAttributes()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link TemplateAttribute} with all parameters set in the given
+	 * JSON body of the request
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param tplRootId
+	 *            id of the {@link TemplateRoot}
+	 * @param tplCompId
+	 *            id of the {@link TemplateComponent}
+	 * @param tplSensorId
+	 *            id of the {@link TemplateSensor}
+	 * @param id
+	 *            the identifier of the {@link TemplateAttribute} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link TemplateAttribute}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID4 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplRootId, @PathParam(REQUESTPARAM_ID2) String tplCompId,
+			@PathParam(REQUESTPARAM_ID3) String tplSensorId, @PathParam(REQUESTPARAM_ID4) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), TemplateAttribute.class, V(id), V(ContextType.TESTEQUIPMENT),
+								SL(tplRootId, tplCompId, tplSensorId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/*
+	 * Delete not implemented as TemplateSensorAttributes can't be deleted
+	 */
+
+	/**
+	 * Returns the search attributes for the {@link TemplateAttribute} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateAttribute.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateAttribute.class, entityService);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateSensorResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateSensorResource.java
new file mode 100755
index 0000000..1761e99
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateSensorResource.java
@@ -0,0 +1,228 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_CATALOGSENSOR_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_QUANTITY_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID3;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Quantity;
+import org.eclipse.mdm.api.dflt.model.CatalogSensor;
+import org.eclipse.mdm.api.dflt.model.TemplateComponent;
+import org.eclipse.mdm.api.dflt.model.TemplateSensor;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.Lazy;
+import io.vavr.collection.List;
+
+/**
+ * {@link TemplateSensor} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tplroots/testequipment/{" + REQUESTPARAM_ID + "}/tplcomps/{"
+		+ REQUESTPARAM_ID2 + "}/tplsensors")
+public class TemplateSensorResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link TemplateSensor}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            id of the {@link TemplateSensor}
+	 * @return the found {@link TemplateSensor} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplRootId, @PathParam(REQUESTPARAM_ID2) String tplCompId,
+			@PathParam(REQUESTPARAM_ID3) String id) {
+		return entityService
+				.find(V(sourceName), TemplateSensor.class, V(id), V(ContextType.TESTEQUIPMENT), SL(tplRootId, tplCompId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link TemplateSensor}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link TemplateSensor} result
+	 * @return the (filtered) {@link TemplateSensor}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplRootId, @PathParam(REQUESTPARAM_ID2) String tplCompId,
+			@QueryParam("filter") String filter) {
+		return entityService
+				.find(V(sourceName), TemplateComponent.class, V(tplCompId), V(ContextType.TESTEQUIPMENT), SL(tplRootId))
+				.map(tplComp -> List.ofAll(tplComp.getTemplateSensors()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link TemplateSensorValue}.
+	 * 
+	 * @param body
+	 *            The {@link TemplateSensor} to create.
+	 * @return the created {@link TemplateSensor} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplRootId, @PathParam(REQUESTPARAM_ID2) String tplCompId, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		// TODO anehmer on 2017-11-25: test that tplComp is only once fetched
+		// get memoized (Lazy) tplComp as it is used twiced and should only fetched once
+		Lazy<TemplateComponent> tplCompSupplier = Lazy.of(() -> entityService
+				.find(V(sourceName), TemplateComponent.class, V(tplCompId), V(ContextType.TESTEQUIPMENT), SL(tplRootId))
+				.get());
+
+		return entityService
+				.create(V(sourceName), TemplateSensor.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME), tplCompSupplier,
+						entityService.find(V(sourceName), CatalogSensor.class,
+								requestBody.getStringValueSupplier(ENTITYATTRIBUTE_CATALOGSENSOR_ID),
+								V(ContextType.TESTEQUIPMENT),
+								SL(tplCompSupplier.map(tplComp -> tplComp.getCatalogComponent()
+										.getID()))),
+						entityService.find(V(sourceName), Quantity.class,
+										requestBody.getStringValueSupplier(ENTITYATTRIBUTE_QUANTITY_ID))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link TemplateSensor} with all parameters set in the given JSON
+	 * body of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link TemplateSensor} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link TemplateSensor}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplRootId, @PathParam(REQUESTPARAM_ID2) String tplCompId,
+			@PathParam(REQUESTPARAM_ID3) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), TemplateSensor.class, V(id),
+								V(ContextType.TESTEQUIPMENT), SL(tplRootId, tplCompId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link TemplateSensor}.
+	 * 
+	 * @param id
+	 *            The identifier of the {@link TemplateSensor} to delete.
+	 * @return the deleted {@link TemplateSensor }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID3 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplRootId, @PathParam(REQUESTPARAM_ID2) String tplCompId,
+			@PathParam(REQUESTPARAM_ID3) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), TemplateSensor.class, V(id),
+								V(ContextType.TESTEQUIPMENT), SL(tplRootId, tplCompId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link TemplateSensor} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateSensor.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateSensor.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestResource.java
new file mode 100644
index 0000000..d333397
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestResource.java
@@ -0,0 +1,187 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.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.dflt.model.TemplateTest;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+/**
+ * {@link TemplateTest} resource handling REST requests
+ * 
+ * @author Gunnar Schmidt, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tpltests")
+public class TemplateTestResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link TemplateTest}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            id of the {@link TemplateTest}
+	 * @return the found {@link TemplateTest} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.find(V(sourceName), TemplateTest.class, V(id))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link TemplateTest}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link TemplateTest} result
+	 * @return the (filtered) {@link TemplateTest}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@QueryParam("filter") String filter) {
+		return entityService.findAll(V(sourceName), TemplateTest.class, filter)
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link TemplateTestValue}.
+	 * 
+	 * @param body
+	 *            The {@link TemplateTest} to create.
+	 * @return the created {@link TemplateTest} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), TemplateTest.class, L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link TemplateTest} with all parameters set in the given JSON
+	 * body of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link TemplateTest} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link TemplateTest}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName), entityService.find(V(sourceName), TemplateTest.class, V(id)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link TemplateTest}.
+	 * 
+	 * @param id
+	 *            The identifier of the {@link TemplateTest} to delete.
+	 * @return the deleted {@link TemplateTest }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.delete(V(sourceName), entityService.find(V(sourceName), TemplateTest.class, V(id)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link TemplateTest} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateTest.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateTest.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepResource.java
new file mode 100644
index 0000000..cb75d24
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepResource.java
@@ -0,0 +1,196 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_CONTEXTTYPE;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStep;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+/**
+ * {@link TemplateTestStep} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tplteststeps")
+public class TemplateTestStepResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link TemplateTestStep}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateTestStep} to load
+	 * @param id
+	 *            id of the {@link TemplateTestStep}
+	 * @return the found {@link TemplateTestStep} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.find(V(sourceName), TemplateTestStep.class, V(id))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link TemplateTestStep}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param contextType
+	 *            {@link ContextType} of the {@link TemplateTestStep} to load
+	 * @param filter
+	 *            filter string to filter the {@link TemplateTestStep} result
+	 * @return the (filtered) {@link TemplateTestStep}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @QueryParam("filter") String filter) {
+		return entityService.findAll(V(sourceName), TemplateTestStep.class, filter)
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link TemplateTestStepValue}.
+	 * 
+	 * @param body
+	 *            The {@link TemplateTestStep} to create.
+	 * @return the created {@link TemplateTestStep} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), TemplateTestStep.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link TemplateTestStep} with all parameters set in the given
+	 * JSON body of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link TemplateTestStep} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link TemplateTestStep}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName), entityService.find(V(sourceName), TemplateTestStep.class, V(id)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link TemplateTestStep}.
+	 * 
+	 * @param id
+	 *            The identifier of the {@link TemplateTestStep} to delete.
+	 * @return the deleted {@link TemplateTestStep }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.delete(V(sourceName), entityService.find(V(sourceName), TemplateTestStep.class, V(id)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link TemplateTestStep} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateTestStep.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateTestStep.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepUsageResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepUsageResource.java
new file mode 100644
index 0000000..207f62a
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepUsageResource.java
@@ -0,0 +1,210 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_TEMPLATETESTSTEP_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.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.dflt.model.TemplateTest;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStep;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStepUsage;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+
+/**
+ * {@link TemplateTestStepUsage} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/tpltests/{" + REQUESTPARAM_ID + "}/tplteststepusages")
+public class TemplateTestStepUsageResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link TemplateTestStep}.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            id of the {@link TemplateTestStep}
+	 * @return the found {@link TemplateTestStep} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplTestId, @PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService.find(V(sourceName), TemplateTestStepUsage.class, V(id), SL(tplTestId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link TemplateTestStepUsage}s.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link TemplateTestStepUsage} result
+	 * @return the (filtered) {@link TemplateTestStepUsage}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplTestId, @QueryParam("filter") String filter) {
+		return entityService.find(V(sourceName), TemplateTest.class, V(tplTestId))
+				.map(tplTest -> List.ofAll(tplTest.getTemplateTestStepUsages()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link TemplateTestStepUsageValue}.
+	 * 
+	 * 
+	 * @param body
+	 *            The {@link TemplateTestStepUsage} to create.
+	 * @return the created {@link TemplateTestStepUsage} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplTestId, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), TemplateTestStepUsage.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+								entityService.find(V(sourceName), TemplateTest.class, V(tplTestId)),
+								entityService.find(V(sourceName), TemplateTestStep.class,
+										requestBody.getStringValueSupplier(ENTITYATTRIBUTE_TEMPLATETESTSTEP_ID))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link TemplateTestUsage} with all parameters set in the given
+	 * JSON body of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link TemplateTestUsage} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link TemplateTestUsage}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplTestId, @PathParam(REQUESTPARAM_ID2) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName),
+						entityService.find(V(sourceName), TemplateTestStepUsage.class, V(id), SL(tplTestId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link TemplateTestStepUsage}.
+	 * 
+	 * 
+	 * @param id
+	 *            The identifier of the {@link TemplateTestStepUsage} to delete.
+	 * @return the deleted {@link TemplateTestStepUsage }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String tplTestId, @PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService
+				.delete(V(sourceName),
+						entityService.find(V(sourceName), TemplateTestStepUsage.class, V(id), SL(tplTestId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link TemplateTestStepUsage} type.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), TemplateTestStepUsage.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), TemplateTestStepUsage.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestResource.java
new file mode 100644
index 0000000..2419f5e
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestResource.java
@@ -0,0 +1,143 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Test;
+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 Test} resource
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Path("/environments/{SOURCENAME}/tests")
+public class TestResource {
+
+	private static final Logger LOG = LoggerFactory.getLogger(TestResource.class);
+
+	@EJB
+	private TestService testService;
+
+	/**
+	 * delegates the request to the {@link TestService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link Test} result
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getTests(@PathParam("SOURCENAME") String sourceName, @QueryParam("filter") String filter) {
+		try {
+			List<Test> tests = this.testService.getTests(sourceName, filter);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Test.class, tests), 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 TestService}
+	 * 
+	 * @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.testService.getSearchAttributes(sourceName);
+			return ServiceUtils.toResponse(new SearchAttributeResponse(searchAttributes), 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 TestService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testId
+	 *            id of the {@link Test}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{TEST_ID}")
+	public Response getTest(@PathParam("SOURCENAME") String sourceName, @PathParam("TEST_ID") String testId) {
+		try {
+			Test test = this.testService.getTest(sourceName, testId);
+			return ServiceUtils.toResponse(new MDMEntityResponse(Test.class, test), 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 TestService}
+	 * 
+	 * @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.testService.localizeAttributes(sourceName);
+			Map<EntityType, String> localizedEntityTypeMap = this.testService.localizeType(sourceName);
+			return ServiceUtils.toResponse(new I18NResponse(localizedEntityTypeMap, localizedAttributeMap), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), 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/TestService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestService.java
new file mode 100644
index 0000000..b13497e
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestService.java
@@ -0,0 +1,146 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Test;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+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;
+
+/**
+ * TestService Bean implementation with available {@link Test} operations
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class TestService {
+
+	@Inject
+	private ConnectorService connectorService;
+	@EJB
+	private I18NActivity i18nActivity;
+	@EJB
+	private SearchActivity searchActivity;
+	@EJB
+	private NavigationActivity navigationActivity;
+
+	/**
+	 * returns the matching {@link Test}s using the given filter or all
+	 * {@link Test}s if no filter is available
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link Test} result
+	 * @return the found {@link Test}s
+	 */
+	public List<Test> getTests(String sourceName, String filter) {
+
+		try {
+			ApplicationContext context = this.connectorService.getContextByName(sourceName);
+			EntityManager em = context
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+
+			if (filter == null || filter.trim().length() <= 0) {
+				return em.loadAll(Test.class);
+			}
+
+			if (ServiceUtils.isParentFilter(context, filter, Pool.class)) {
+				String id = ServiceUtils.extactIdFromParentFilter(context, filter, Pool.class);
+				return this.navigationActivity.getTests(sourceName, id);
+			}
+
+			return this.searchActivity.search(context, Test.class, filter);
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * Returns the {@link SearchAttribute} for the entity type Test 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) {
+		return this.searchActivity.listAvailableAttributes(this.connectorService.getContextByName(sourceName), Test.class);
+	}
+
+	/**
+	 * returns a {@link Test} identified by the given id.
+	 * 
+	 * @param testId
+	 *            id of the {@link Test}
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link Test}
+	 * @return the matching {@link Test}
+	 */
+	public Test getTest(String sourceName, String testId) {
+		try {
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			return em.load(Test.class, testId);
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns localized {@link Test} attributes
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Test} attributes
+	 */
+	public Map<Attribute, String> localizeAttributes(String sourceName) {
+		return this.i18nActivity.localizeAttributes(sourceName, Test.class);
+	}
+
+	/**
+	 * returns the localized {@link Test} type name
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link Test} type name
+	 */
+	public Map<EntityType, String> localizeType(String sourceName) {
+		return this.i18nActivity.localizeType(sourceName, Test.class);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestStepResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestStepResource.java
new file mode 100644
index 0000000..da9e7e6
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestStepResource.java
@@ -0,0 +1,273 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.ContextRoot;
+import org.eclipse.mdm.api.base.model.ContextSensor;
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.businessobjects.entity.ContextResponse;
+import org.eclipse.mdm.businessobjects.entity.ContextSensorResponse;
+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 TestStep} resource
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Path("/environments/{SOURCENAME}/teststeps")
+public class TestStepResource {
+
+	private static final Logger LOG = LoggerFactory.getLogger(TestStepResource.class);
+
+	@EJB
+	private TestStepService testStepService;
+
+	/**
+	 * delegates the request to the {@link TestStepService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the TestStep result
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getTestSteps(@PathParam("SOURCENAME") String sourceName, @QueryParam("filter") String filter) {
+
+		try {
+			List<TestStep> testSteps = this.testStepService.getTestSteps(sourceName, filter);
+			return ServiceUtils.toResponse(new MDMEntityResponse(TestStep.class, testSteps), 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 TestStepService}
+	 * 
+	 * @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.testStepService.getSearchAttributes(sourceName);
+			return ServiceUtils.toResponse(new SearchAttributeResponse(searchAttributes), 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 TestStepService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{TESTSTEP_ID}")
+	public Response getTestStep(@PathParam("SOURCENAME") String sourceName, @PathParam("TESTSTEP_ID") String testStepId) {
+
+		try {
+			TestStep testStep = this.testStepService.getTestStep(sourceName, testStepId);
+			return ServiceUtils.toResponse(new MDMEntityResponse(TestStep.class, testStep), 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 TestStepService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{TESTSTEP_ID}/contexts")
+	public Response getContext(@PathParam("SOURCENAME") String sourceName, @PathParam("TESTSTEP_ID") String testStepId) {
+		try {
+			Map<String, Map<ContextType, ContextRoot>> contextMap = this.testStepService.getContext(sourceName,
+					testStepId);
+
+			return ServiceUtils.toResponse(new ContextResponse(contextMap), 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 TestStepService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{TESTSTEP_ID}/contexts/unitundertest")
+	public Response getContextUUT(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("TESTSTEP_ID") String testStepId) {
+		try {
+			Map<String, Map<ContextType, ContextRoot>> contextMap = this.testStepService.getContextUUT(sourceName,
+					testStepId);
+			return ServiceUtils.toResponse(new ContextResponse(contextMap), 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 TestStepService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{TESTSTEP_ID}/contexts/testsequence")
+	public Response getContextTSQ(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("TESTSTEP_ID") String testStepId) {
+		try {
+			Map<String, Map<ContextType, ContextRoot>> contextMap = this.testStepService.getContextTSQ(sourceName,
+					testStepId);
+			return ServiceUtils.toResponse(new ContextResponse(contextMap), 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 TestStepService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{TESTSTEP_ID}/contexts/testequipment")
+	public Response getContextTEQ(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("TESTSTEP_ID") String testStepId) {
+		try {
+			Map<String, Map<ContextType, ContextRoot>> contextMap = this.testStepService.getContextTEQ(sourceName,
+					testStepId);
+			return ServiceUtils.toResponse(new ContextResponse(contextMap), 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 TestStepService}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{TESTSTEP_ID}/contexts/testequipment/sensors")
+	public Response getContextTEQSensors(@PathParam("SOURCENAME") String sourceName,
+			@PathParam("TESTSTEP_ID") String testStepId) {
+		try {
+			Map<String, List<ContextSensor>> sensorMap = this.testStepService.getSensors(sourceName, testStepId);
+			return ServiceUtils.toResponse(new ContextSensorResponse(sensorMap), 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 TestStepService}
+	 * 
+	 * @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.testStepService.localizeAttributes(sourceName);
+			Map<EntityType, String> localizedEntityTypeMap = this.testStepService.localizeType(sourceName);
+			return ServiceUtils.toResponse(new I18NResponse(localizedEntityTypeMap, localizedAttributeMap), Status.OK);
+
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), 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/TestStepService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestStepService.java
new file mode 100644
index 0000000..e715f5a
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TestStepService.java
@@ -0,0 +1,221 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.ContextRoot;
+import org.eclipse.mdm.api.base.model.ContextSensor;
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.businessobjects.control.ContextActivity;
+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;
+
+/**
+ * TestStepService Bean implementation with available {@link TestStep}
+ * operations
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class TestStepService {
+
+	@Inject
+	private ConnectorService connectorService;
+	@EJB
+	private I18NActivity i18nActivity;
+	@EJB
+	private NavigationActivity navigationActivity;
+	@EJB
+	private ContextActivity contextActivity;
+	@EJB
+	private SearchActivity searchActivity;
+
+	/**
+	 * returns the matching {@link TestStep}s using the given filter or all
+	 * {@link TestStep}s if no filter is available
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the TestStep result
+	 * @return the found {@link TestStep}s
+	 */
+	public List<TestStep> getTestSteps(String sourceName, String filter) {
+		try {
+
+			ApplicationContext context = this.connectorService.getContextByName(sourceName);
+			EntityManager em = context
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+
+			if (filter == null || filter.trim().length() <= 0) {
+				return em.loadAll(TestStep.class);
+			}
+
+			if (ServiceUtils.isParentFilter(context, filter, TestStep.PARENT_TYPE_TEST)) {
+				String id = ServiceUtils.extactIdFromParentFilter(context, filter, TestStep.PARENT_TYPE_TEST);
+				return this.navigationActivity.getTestSteps(sourceName, id);
+			}
+
+			return this.searchActivity.search(context, TestStep.class, filter);
+
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * Returns the {@link SearchAttribute} for the entity type TestStep 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) {
+		return this.searchActivity.listAvailableAttributes(this.connectorService.getContextByName(sourceName), TestStep.class);
+	}
+
+	/**
+	 * returns a {@link TestStep} identified by the given id.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return the matching {@link TestStep}
+	 */
+	public TestStep getTestStep(String sourceName, String testStepId) {
+		try {
+			return this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.map(em -> em.load(TestStep.class, testStepId))
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns the complete context data (ordered and measured) for a
+	 * {@link TestStep}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return a map with the complete context data (ordered and measured)
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getContext(String sourceName, String testStepId) {
+		return this.contextActivity.getTestStepContext(sourceName, testStepId);
+	}
+
+	/**
+	 * returns the UnitUnderTest context data (ordered and measured) for a
+	 * {@link TestStep}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return a map with the UnitUnderTest context data (ordered and measured)
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getContextUUT(String sourceName, String testStepId) {
+		return this.contextActivity.getTestStepContext(sourceName, testStepId, ContextType.UNITUNDERTEST);
+	}
+
+	/**
+	 * returns the TestSequence context data (ordered and measured) for a
+	 * {@link TestStep}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return a map with the TestSequence context data (ordered and measured)
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getContextTSQ(String sourceName, String testStepId) {
+		return this.contextActivity.getTestStepContext(sourceName, testStepId, ContextType.TESTSEQUENCE);
+	}
+
+	/**
+	 * returns the TestEquipment context data (ordered and measured) for a
+	 * {@link TestStep}
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return a map with the TestEquipment context data (ordered and measured)
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getContextTEQ(String sourceName, String testStepId) {
+		return this.contextActivity.getTestStepContext(sourceName, testStepId, ContextType.TESTEQUIPMENT);
+	}
+
+	/**
+	 * returns all sensor context data of TestEquipment sensor configuration
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param testStepId
+	 *            id of the {@link TestStep}
+	 * @return a map with the TestEquipment sensor context data (ordered and
+	 *         measured)
+	 */
+	public Map<String, List<ContextSensor>> getSensors(String sourceName, String testStepId) {
+		return this.contextActivity.getTestStepSensorContext(sourceName, testStepId);
+	}
+
+	/**
+	 * returns localized {@link TestStep} attributes
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link TestStep} attributes
+	 */
+	public Map<Attribute, String> localizeAttributes(String sourceName) {
+		return this.i18nActivity.localizeAttributes(sourceName, TestStep.class);
+	}
+
+	/**
+	 * returns the localized {@link TestStep} type name
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the localized {@link TestStep} type name
+	 */
+	public Map<EntityType, String> localizeType(String sourceName) {
+		return this.i18nActivity.localizeType(sourceName, TestStep.class);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/UnitResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/UnitResource.java
new file mode 100644
index 0000000..fe917b7
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/UnitResource.java
@@ -0,0 +1,196 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_PHYSICALDIMENSION_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.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.model.PhysicalDimension;
+import org.eclipse.mdm.api.base.model.Unit;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+/**
+ * {@link Unit} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/units")
+public class UnitResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link Unit}. {@link WebApplicationException} on error.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            id of the {@link Unit}
+	 * @return the found {@link Unit} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.find(V(sourceName), Unit.class, V(id))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link Unit}s.
+	 * 
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link Unit} result
+	 * @return the (filtered) {@link Unit}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@QueryParam("filter") String filter) {
+		return entityService.findAll(V(sourceName), Unit.class, filter)
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link Unit}.
+	 * 
+	 * 
+	 * @param body
+	 *            The {@link Unit} to create.
+	 * @return the created {@link Unit} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), Unit.class, L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+						entityService.find(V(sourceName), PhysicalDimension.class,
+								requestBody.getStringValueSupplier(ENTITYATTRIBUTE_PHYSICALDIMENSION_ID))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link Unit} with all parameters set in the given JSON body of
+	 * the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link Unit} to update.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link Unit}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName), entityService.find(V(sourceName), Unit.class, V(id)), requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link Unit}. {@link WebApplicationException}
+	 * on error.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            The identifier of the {@link Unit} to delete.
+	 * @return the deleted {@link Unit }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.delete(V(sourceName), entityService.find(V(sourceName), Unit.class, V(id)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link Unit} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), Unit.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), Unit.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ValueListResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ValueListResource.java
new file mode 100644
index 0000000..c551f8a
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ValueListResource.java
@@ -0,0 +1,192 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.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.dflt.model.ValueList;
+import org.eclipse.mdm.api.dflt.model.ValueListValue;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+/**
+ * {@link ValueList} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/valuelists")
+public class ValueListResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link ValueList}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            id of the {@link ValueList}
+	 * @return the found {@link ValueList} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.find(V(sourceName), ValueList.class, V(id))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link ValueList}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link ValueList} result
+	 * @return the (filtered) {@link ValueList}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@QueryParam("filter") String filter) {
+		return entityService.findAll(V(sourceName), ValueList.class, filter)
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link ValueList}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param body
+	 *            The {@link ValueList} to create.
+	 * @return the created {@link ValueList} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), ValueList.class, L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link ValueList} with all parameters set in the given JSON body
+	 * of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link ValueListValue} to update.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link ValueList}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id,
+			String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName), entityService.find(V(sourceName), ValueList.class, V(id)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link ValueList}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            The identifier of the {@link ValueList} to delete.
+	 * @return the deleted {@link ValueList }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String id) {
+		return entityService.delete(V(sourceName), entityService.find(V(sourceName), ValueList.class, V(id)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link ValueList} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), ValueList.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), ValueList.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ValueListValueResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ValueListValueResource.java
new file mode 100644
index 0000000..58d3162
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ValueListValueResource.java
@@ -0,0 +1,204 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.ENTITYATTRIBUTE_NAME;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID2;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
+import static org.eclipse.mdm.businessobjects.service.EntityService.L;
+import static org.eclipse.mdm.businessobjects.service.EntityService.SL;
+import static org.eclipse.mdm.businessobjects.service.EntityService.V;
+
+import javax.ejb.EJB;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+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.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.dflt.model.ValueList;
+import org.eclipse.mdm.api.dflt.model.ValueListValue;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.utils.RequestBody;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import io.vavr.collection.List;
+/**
+ * {@link ValueListValue} resource handling REST requests
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Path("/environments/{" + REQUESTPARAM_SOURCENAME + "}/valuelists/{" + REQUESTPARAM_ID + "}/values")
+public class ValueListValueResource {
+
+	@EJB
+	private EntityService entityService;
+
+	/**
+	 * Returns the found {@link ValueListValue}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            id of the {@link ValueListValue}
+	 * @return the found {@link ValueListValue} as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String valueListId, @PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService.find(V(sourceName), ValueListValue.class, V(id), SL(valueListId))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the (filtered) {@link ValueListValue}s.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param filter
+	 *            filter string to filter the {@link ValueListValue} result
+	 * @return the (filtered) {@link ValueListValue}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String valueListId, @QueryParam("filter") String filter) {
+
+		return entityService.find(V(sourceName), ValueList.class, V(valueListId))
+				.map(valueList -> List.ofAll(valueList.getValueListValues()))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the created {@link ValueListValue}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param valueListId
+	 *            id of the {@link ValueList} to create to value for
+	 * @param body
+	 *            The {@link ValueListValue} to create.
+	 * @return the created {@link ValueListValue} as {@link Response}.
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String valueListId, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.create(V(sourceName), ValueListValue.class,
+						L(requestBody.getStringValueSupplier(ENTITYATTRIBUTE_NAME),
+								entityService.find(V(sourceName), ValueList.class, V(valueListId))))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Updates the {@link ValueListValue} with all parameters set in the given JSON
+	 * body of the request.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            the identifier of the {@link ValueListValue} to delete.
+	 * @param body
+	 *            the body of the request containing the attributes to update
+	 * @return the updated {@link ValueListValue}
+	 */
+	@PUT
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response update(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String valueListId, @PathParam(REQUESTPARAM_ID2) String id, String body) {
+		RequestBody requestBody = RequestBody.create(body);
+
+		return entityService
+				.update(V(sourceName), entityService.find(V(sourceName), ValueListValue.class, V(id), SL(valueListId)),
+						requestBody.getValueMapSupplier())
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Deletes and returns the deleted {@link ValueListValue}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param id
+	 *            The identifier of the {@link ValueListValue} to delete.
+	 * @return the deleted {@link ValueListValue }s as {@link Response}
+	 */
+	@DELETE
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_ID2 + "}")
+	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_ID) String valueListId, @PathParam(REQUESTPARAM_ID2) String id) {
+		return entityService
+				.delete(V(sourceName), entityService.find(V(sourceName), ValueListValue.class, V(id), SL(valueListId)))
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Returns the search attributes for the {@link ValueListValue} type.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the {@link SearchAttribute}s as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/searchattributes")
+	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildSearchAttributesResponse(V(sourceName), ValueListValue.class, entityService);
+	}
+
+	/**
+	 * Returns a map of localization for the entity type and the attributes.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @return the I18N as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/localizations")
+	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
+		return ServiceUtils.buildLocalizationResponse(V(sourceName), ValueListValue.class, entityService);
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/ContextActivity.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/ContextActivity.java
new file mode 100644
index 0000000..28b38f7
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/ContextActivity.java
@@ -0,0 +1,267 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.model.ContextComponent;
+import org.eclipse.mdm.api.base.model.ContextDescribable;
+import org.eclipse.mdm.api.base.model.ContextRoot;
+import org.eclipse.mdm.api.base.model.ContextSensor;
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Measurement;
+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.connector.boundary.ConnectorService;
+
+/**
+ * ContextActivity Bean implementation to get ordered and measured context data
+ * for {@link ContextDescribable} business objects (e.g. TestStep)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class ContextActivity implements Serializable {
+
+	public static final String CONTEXT_GROUP_ORDERED = "ordered";
+	public static final String CONTEXT_GROUP_MEASURED = "measured";
+
+	public static final String CONTEXT_SENSOR_GROUP_ORDERED = "sensor_ordered";
+	public static final String CONTEXT_SENSOR_GROUP_MEASURED = "sensor_measured";
+
+	@Inject
+	private ConnectorService connectorService;
+
+	/**
+	 * returns the ordered and measurement context for a {@link TestStep} MDM
+	 * business object identified by the given MDM system name and
+	 * {@link TestStep} ID. If no {@link ContextType}s are defined for this
+	 * method call, the method returns all context informations of the available
+	 * {@link ContextType}s. Otherwise you can specify a list of
+	 * {@link ContextType}s.
+	 * 
+	 * Possible {@link ContextType}s are {@link ContextType}.UNITUNDERTEST,
+	 * {@link ContextType}.TESTSEQUENCE and {@link ContextType}.TESTEQUIPMENT.
+	 * 
+	 * @param sourceName
+	 *            the MDM system name
+	 * @param testStepID
+	 *            instance id if the {@link TestStep}
+	 * @param contextTypes
+	 *            list of {@link ContextType}s
+	 * @return the ordered and measured context data as context object for the
+	 *         identified {@link TestStep}
+	 * @throws ContextProviderException
+	 *             if an error occurs during lookup the context informations
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getTestStepContext(String sourceName, String testStepID,
+			ContextType... contextTypes) {
+		try {
+
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			TestStep testStep = em.load(TestStep.class, testStepID);
+
+			Map<ContextType, ContextRoot> orderedContext = em.loadContexts(testStep, contextTypes);
+			Map<ContextType, ContextRoot> measuredContext = lookupMeasuredContextByTestStep(em, testStep, contextTypes);
+
+			Map<String, Map<ContextType, ContextRoot>> contextMap = new HashMap<>();
+			contextMap.put(CONTEXT_GROUP_ORDERED, orderedContext);
+			contextMap.put(CONTEXT_GROUP_MEASURED, measuredContext);
+
+			return contextMap;
+
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns the ordered and measurement context for a {@link Measurement} MDM
+	 * business object identified by the given MDM system name and
+	 * {@link Measurement} ID. If no {@link ContextType}s are defined for this
+	 * method call, the method returns all context informations of the available
+	 * {@link ContextType}s. Otherwise you can specify a list of
+	 * {@link ContextType}s.
+	 * 
+	 * Possible {@link ContextType}s are {@link ContextType}.UNITUNDERTEST,
+	 * {@link ContextType}.TESTSEQUENCE and {@link ContextType}.TESTEQUIPMENT.
+	 * 
+	 * @param sourceName
+	 *            the MDM system name
+	 * @param measurementID
+	 *            instance id if the {@link Measurement}
+	 * @param contextTypes
+	 *            list of {@link ContextType}s
+	 * @return the ordered and measured context data as context object for the
+	 *         identified {@link Measurement}
+	 * @throws ContextProviderException
+	 *             if an error occurs during lookup the context informations
+	 */
+	public Map<String, Map<ContextType, ContextRoot>> getMeasurementContext(String sourceName, String measurementID,
+			ContextType... contextTypes) {
+
+		try {
+
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			Measurement measurement = em.load(Measurement.class, measurementID);
+
+			Map<ContextType, ContextRoot> measuredContext = em.loadContexts(measurement, contextTypes);
+			Map<ContextType, ContextRoot> orderedContext = lookupOrderedContextByMeasurement(em, measurement,
+					contextTypes);
+
+			Map<String, Map<ContextType, ContextRoot>> contextMap = new HashMap<>();
+			contextMap.put(CONTEXT_GROUP_ORDERED, orderedContext);
+			contextMap.put(CONTEXT_GROUP_MEASURED, measuredContext);
+
+			return contextMap;
+
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+
+	}
+
+	/**
+	 * returns the sensor context of the given {@link TestStep}. The sensor
+	 * informations will extracted from the TestEquipment part of the context
+	 * data.
+	 * 
+	 * @param sourceName
+	 *            the MDM system name
+	 * @param testStepID
+	 *            instance id if the {@link TestStep}
+	 * @return a map with the TestEquipment sensor context data (ordered and
+	 *         measured)
+	 */
+	public Map<String, List<ContextSensor>> getTestStepSensorContext(String sourceName, String testStepID) {
+
+		try {
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			
+			TestStep testStep = em.load(TestStep.class, testStepID);
+
+			Map<ContextType, ContextRoot> orderedContext = em.loadContexts(testStep, ContextType.TESTEQUIPMENT);
+			Map<ContextType, ContextRoot> measuredContext = lookupMeasuredContextByTestStep(em, testStep,
+					ContextType.TESTEQUIPMENT);
+
+			return createSensorMap(orderedContext, measuredContext);
+
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * returns the sensor context of the given {@link Measurement}. The sensor
+	 * informations will extracted from the TestEquipment part of the context
+	 * data.
+	 * 
+	 * @param sourceName
+	 *            the MDM system name
+	 * @param measurementID
+	 *            instance id if the {@link Measurement}
+	 * @return a map with the TestEquipment sensor context data (ordered and
+	 *         measured)
+	 */
+	public Map<String, List<ContextSensor>> getMeasurementSensorContext(String sourceName, String measurementID) {
+		try {
+
+			EntityManager em = this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"));
+			Measurement measurement = em.load(Measurement.class, measurementID);
+
+			Map<ContextType, ContextRoot> measuredContext = em.loadContexts(measurement, ContextType.TESTEQUIPMENT);
+			Map<ContextType, ContextRoot> orderedContext = lookupOrderedContextByMeasurement(em, measurement,
+					ContextType.TESTEQUIPMENT);
+
+			return createSensorMap(orderedContext, measuredContext);
+
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	private Map<ContextType, ContextRoot> lookupOrderedContextByMeasurement(EntityManager em, Measurement measurement,
+			ContextType... contextTypes) throws DataAccessException {
+
+		Optional<TestStep> optional = em.loadParent(measurement, Measurement.PARENT_TYPE_TESTSTEP);
+
+		if (!optional.isPresent()) {
+			return Collections.emptyMap();
+		}
+
+		TestStep testStep = optional.get();
+		return em.loadContexts(testStep, contextTypes);
+	}
+
+	private Map<ContextType, ContextRoot> lookupMeasuredContextByTestStep(EntityManager em, TestStep testStep,
+			ContextType... contextTypes) throws DataAccessException {
+
+		List<Measurement> childList = em.loadChildren(testStep, TestStep.CHILD_TYPE_MEASUREMENT);
+		if (childList.size() > 0) {
+			return em.loadContexts(childList.get(0), contextTypes);
+		}
+		return Collections.emptyMap();
+	}
+
+	private Map<String, List<ContextSensor>> createSensorMap(Map<ContextType, ContextRoot> orderedContext,
+			Map<ContextType, ContextRoot> measuredContext) {
+
+		Map<String, List<ContextSensor>> sensorMap = new HashMap<String, List<ContextSensor>>();
+
+		ContextRoot orderedContextRoot = orderedContext.get(ContextType.TESTEQUIPMENT);
+		if (orderedContextRoot != null) {
+			List<ContextSensor> orderedSensors = extractContextSensors(orderedContextRoot);
+			sensorMap.put(CONTEXT_SENSOR_GROUP_ORDERED, orderedSensors);
+		}
+
+		ContextRoot measuredContextRoot = measuredContext.get(ContextType.TESTEQUIPMENT);
+		if (measuredContextRoot != null) {
+			List<ContextSensor> measuredSensors = extractContextSensors(measuredContextRoot);
+			sensorMap.put(CONTEXT_SENSOR_GROUP_MEASURED, measuredSensors);
+		}
+
+		return sensorMap;
+	}
+
+	private List<ContextSensor> extractContextSensors(ContextRoot contextRoot) {
+		List<ContextSensor> contextSensors = new ArrayList<ContextSensor>();
+		List<ContextComponent> contextComponents = contextRoot.getContextComponents();
+		for (ContextComponent contextComponent : contextComponents) {
+			contextSensors.addAll(contextComponent.getContextSensors());
+		}
+		return contextSensors;
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/FilterParser.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/FilterParser.java
new file mode 100644
index 0000000..4942489
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/FilterParser.java
@@ -0,0 +1,495 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+import org.antlr.v4.runtime.misc.ParseCancellationException;
+import org.antlr.v4.runtime.tree.TerminalNode;
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.ComparisonOperator;
+import org.eclipse.mdm.api.base.query.Filter;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarBaseVisitor;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarLexer;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.AndExpressionContext;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.AttributeContext;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.ComparatorExpressionContext;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.ListComparatorExpressionContext;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.NotExpressionContext;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.OrExpressionContext;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.UnaryComparatorExpressionContext;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.ValueContext;
+import org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.ValuesContext;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Strings;
+import com.google.common.primitives.Booleans;
+import com.google.common.primitives.Bytes;
+import com.google.common.primitives.Doubles;
+import com.google.common.primitives.Floats;
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Longs;
+import com.google.common.primitives.Shorts;
+
+/**
+ * Class for parsing filter strings.
+ * 
+ * @author Matthias Koller
+ *
+ */
+public class FilterParser {
+	
+	private FilterParser() {
+	}
+	
+	/**
+	 * Visitor class to convert the parsed tree into a {@link Filter}.
+	 */
+	private static final class FilterVisitor extends FilterGrammarBaseVisitor<Filter> {
+		private List<EntityType> availableEntityTypes;
+
+		/**
+		 * Constructs a new Visitor operating on the given search attributes.
+		 * 
+		 * @param availableEntityTypes
+		 *            List of {@link EntityType}s to match the parsed attributes
+		 *            against.
+		 */
+		private FilterVisitor(List<EntityType> availableEntityTypes) {
+			this.availableEntityTypes = availableEntityTypes;
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.mdm.businessobjects.filter.FilterGrammarBaseVisitor#
+		 * visitAndExpression(org.eclipse.mdm.businessobjects.filter.FilterGrammarParser
+		 * .AndExpressionContext)
+		 */
+		@Override
+		public Filter visitAndExpression(AndExpressionContext ctx) {
+			return Filter.and().merge(visit(ctx.left), visit(ctx.right));
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.mdm.businessobjects.filter.FilterGrammarBaseVisitor#
+		 * visitOrExpression(org.eclipse.mdm.businessobjects.filter.FilterGrammarParser.
+		 * OrExpressionContext)
+		 */
+		@Override
+		public Filter visitOrExpression(OrExpressionContext ctx) {
+			return Filter.or().merge(visit(ctx.left), visit(ctx.right));
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.mdm.businessobjects.filter.FilterGrammarBaseVisitor#
+		 * visitNotExpression(org.eclipse.mdm.businessobjects.filter.FilterGrammarParser
+		 * .NotExpressionContext)
+		 */
+		@Override
+		public Filter visitNotExpression(NotExpressionContext ctx) {
+			return super.visitNotExpression(ctx).invert();
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.mdm.businessobjects.filter.FilterGrammarBaseVisitor#
+		 * visitComparatorExpression(org.eclipse.mdm.businessobjects.filter.
+		 * FilterGrammarParser.ComparatorExpressionContext)
+		 */
+		@Override
+		public Filter visitComparatorExpression(ComparatorExpressionContext ctx) {
+			ComparisonOperator operator = getOperator(ctx.op);
+			Attribute attribute = getAttribute(ctx.left);
+			Object value = createConditionValue(attribute.getValueType(), getValue(ctx.right));
+
+			return Filter.and().add(operator.create(attribute, value));
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.mdm.businessobjects.filter.FilterGrammarBaseVisitor#
+		 * visitListComparatorExpression(org.eclipse.mdm.businessobjects.filter.
+		 * FilterGrammarParser.ListComparatorExpressionContext)
+		 */
+		@Override
+		public Filter visitListComparatorExpression(ListComparatorExpressionContext ctx) {
+			ComparisonOperator operator = getOperator(ctx.op);
+			Attribute attribute = getAttribute(ctx.left);
+			Object value = createConditionValues(attribute.getValueType(), getValues(ctx.right));
+
+			return Filter.and().add(operator.create(attribute, value));
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.eclipse.mdm.businessobjects.filter.FilterGrammarBaseVisitor#
+		 * visitUnaryComparatorExpression(org.eclipse.mdm.businessobjects.filter.
+		 * FilterGrammarParser.UnaryComparatorExpressionContext)
+		 */
+		@Override
+		public Filter visitUnaryComparatorExpression(UnaryComparatorExpressionContext ctx) {
+			ComparisonOperator operator = getOperator(ctx.op);
+			Attribute attribute = getAttribute(ctx.left);
+			Object value = createConditionValue(attribute.getValueType(), null);
+
+			return Filter.and().add(operator.create(attribute, value));
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see
+		 * org.antlr.v4.runtime.tree.AbstractParseTreeVisitor#aggregateResult(java.lang.
+		 * Object, java.lang.Object)
+		 */
+		@Override
+		protected Filter aggregateResult(Filter aggregate, Filter nextResult) {
+			if (nextResult == null) {
+				return aggregate;
+			}
+			return super.aggregateResult(aggregate, nextResult);
+		}
+
+		/**
+		 * Extract a string from the given {@link ValueContext}. Basically this methods
+		 * returns a string representation of the value without enclosing quotes (if
+		 * there were any).
+		 * 
+		 * @param ctx
+		 *            {@link ValueContext} containing the parsed value.
+		 * @return string representation of the value given by {@link ValueContext}
+		 */
+		private String getValue(ValueContext ctx) {
+
+			TerminalNode typeNode = (TerminalNode) ctx.getChild(0);
+			switch (typeNode.getSymbol().getType()) {
+			case FilterGrammarLexer.STRINGLITERAL:
+				return CharMatcher.anyOf("'").trimFrom(ctx.STRINGLITERAL().getText()).replaceAll("\\\\'", "'");
+			case FilterGrammarLexer.DECIMAL:
+			case FilterGrammarLexer.LONG:
+			case FilterGrammarLexer.BOOL:
+				return ctx.getText();
+			default:
+				throw new RuntimeException("Unsupported Symbol: " + typeNode.getSymbol().getType());
+			}
+		}
+
+		/**
+		 * Extract a list string from the given {@link ValuesContext}. Basically this
+		 * methods returns a list of string representations of the values without
+		 * enclosing quotes (if there were any).
+		 * 
+		 * @param ctx
+		 *            {@link ValuesContext} containing the parsed values.
+		 * @return string representations of the values given by {@link ValuesContext}
+		 */
+		private List<String> getValues(ValuesContext ctx) {
+			List<String> values = new ArrayList<>();
+			for (org.antlr.v4.runtime.tree.ParseTree child : ctx.children) {
+				if (child instanceof ValueContext) {
+					values.add(getValue((ValueContext) child));
+				}
+			}
+			return values;
+		}
+
+		/**
+		 * Converts an {@link AttributeContext} into an {@link Attribute} using the list
+		 * of available EntityTypes.
+		 * 
+		 * @param ctx
+		 *            parsed entitytype / attribute
+		 * @return the matched {@link Attribute} given by {@link AttributeContext}
+		 * @throws IllegalArgumentException
+		 *             if {@link EntityType} or {@link Attribute} given by
+		 *             <code>ctx</code> cannot be found.
+		 */
+		private Attribute getAttribute(AttributeContext ctx) {
+			String[] name = ctx.getText().split("\\.");
+			return availableEntityTypes.stream()
+					.filter(e -> ServiceUtils.workaroundForTypeMapping(e).equals(name[0]))
+					.findAny()
+					.orElseThrow(() -> new IllegalArgumentException("Entity " + name[0] + " not found in data source!"))
+					.getAttribute(name[1]);
+		}
+
+		/**
+		 * Converts a {@link ParserRuleContext} containing a {@link TerminalNode} into a
+		 * {@link ComparisonOperator}.
+		 * 
+		 * @param ctx
+		 *            {@link UnaryComparatorExpressionContext},
+		 *            {@link ComparatorExpressionContext} or
+		 *            {@link ListComparatorExpressionContext} or
+		 * @return converted {@link ComparisonOperator}
+		 * @throws IllegalArgumentException
+		 *             if the operator given by <code>ctx</code> is unknown or cannot be
+		 *             converted.
+		 */
+		private ComparisonOperator getOperator(ParserRuleContext ctx) {
+			TerminalNode typeNode = (TerminalNode) ctx.getChild(0);
+			switch (typeNode.getSymbol().getType()) {
+			case FilterGrammarLexer.EQUAL:
+				return ComparisonOperator.EQUAL;
+			case FilterGrammarLexer.NOT_EQUAL:
+				return ComparisonOperator.NOT_EQUAL;
+			case FilterGrammarLexer.LESS_THAN:
+				return ComparisonOperator.LESS_THAN;
+			case FilterGrammarLexer.LESS_THAN_OR_EQUAL:
+				return ComparisonOperator.LESS_THAN_OR_EQUAL;
+			case FilterGrammarLexer.GREATER_THAN:
+				return ComparisonOperator.GREATER_THAN;
+			case FilterGrammarLexer.GREATER_THAN_OR_EQUAL:
+				return ComparisonOperator.GREATER_THAN_OR_EQUAL;
+			case FilterGrammarLexer.IN_SET:
+				return ComparisonOperator.IN_SET;
+			case FilterGrammarLexer.NOT_IN_SET:
+				return ComparisonOperator.NOT_IN_SET;
+			case FilterGrammarLexer.LIKE:
+				return ComparisonOperator.LIKE;
+			case FilterGrammarLexer.NOT_LIKE:
+				return ComparisonOperator.NOT_LIKE;
+			case FilterGrammarLexer.CASE_INSENSITIVE_EQUAL:
+				return ComparisonOperator.CASE_INSENSITIVE_EQUAL;
+			case FilterGrammarLexer.CASE_INSENSITIVE_NOT_EQUAL:
+				return ComparisonOperator.CASE_INSENSITIVE_NOT_EQUAL;
+			case FilterGrammarLexer.CASE_INSENSITIVE_LESS_THAN:
+				return ComparisonOperator.CASE_INSENSITIVE_LESS_THAN;
+			case FilterGrammarLexer.CASE_INSENSITIVE_LESS_THAN_OR_EQUAL:
+				return ComparisonOperator.CASE_INSENSITIVE_LESS_THAN_OR_EQUAL;
+			case FilterGrammarLexer.CASE_INSENSITIVE_GREATER_THAN:
+				return ComparisonOperator.CASE_INSENSITIVE_GREATER_THAN;
+			case FilterGrammarLexer.CASE_INSENSITIVE_GREATER_THAN_OR_EQUAL:
+				return ComparisonOperator.CASE_INSENSITIVE_GREATER_THAN_OR_EQUAL;
+			case FilterGrammarLexer.CASE_INSENSITIVE_IN_SET:
+				return ComparisonOperator.CASE_INSENSITIVE_IN_SET;
+			case FilterGrammarLexer.CASE_INSENSITIVE_NOT_IN_SET:
+				return ComparisonOperator.CASE_INSENSITIVE_NOT_IN_SET;
+			case FilterGrammarLexer.CASE_INSENSITIVE_LIKE:
+				return ComparisonOperator.CASE_INSENSITIVE_LIKE;
+			case FilterGrammarLexer.CASE_INSENSITIVE_NOT_LIKE:
+				return ComparisonOperator.CASE_INSENSITIVE_NOT_LIKE;
+			case FilterGrammarLexer.IS_NULL:
+				return ComparisonOperator.IS_NULL;
+			case FilterGrammarLexer.IS_NOT_NULL:
+				return ComparisonOperator.IS_NOT_NULL;
+			case FilterGrammarLexer.BETWEEN:
+				return ComparisonOperator.BETWEEN;
+			default:
+				throw new IllegalArgumentException(
+						"Operator " + typeNode.getSymbol().getType() + " not supported yet!");
+			}
+		}
+	}
+
+	/**
+	 * Class to convert a antlr syntax error into a unchecked
+	 * ParserCancellationException.
+	 */
+	private static class ThrowingErrorListener extends BaseErrorListener {
+
+		public static final ThrowingErrorListener INSTANCE = new ThrowingErrorListener();
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see org.antlr.v4.runtime.BaseErrorListener#syntaxError(org.antlr.v4.runtime.
+		 * Recognizer, java.lang.Object, int, int, java.lang.String,
+		 * org.antlr.v4.runtime.RecognitionException)
+		 */
+		@Override
+		public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine,
+				String msg, RecognitionException e) throws ParseCancellationException {
+			throw new ParseCancellationException("line " + line + ":" + charPositionInLine + " " + msg);
+		}
+	}
+
+	/**
+	 * Parses the given filter string. The filter string must conform to the ANTLR
+	 * grammer defined in FilterGrammar.g4.
+	 * 
+	 * @param possibleEntityTypes
+	 *            The possible {@link EntityType}s / {@link Attribute}s
+	 * @param filterString
+	 *            The filter string to parse.
+	 * @return the parsed {@link Filter}
+	 * @throws IllegalArgumentExceptionThrown
+	 *             if parsing fails.
+	 */
+	public static Filter parseFilterString(List<EntityType> possibleEntityTypes, String filterString)
+			throws IllegalArgumentException {
+
+		if (Strings.isNullOrEmpty(filterString)) {
+			return Filter.and();
+		}
+
+		try {
+			FilterGrammarLexer lexer = new FilterGrammarLexer(new ANTLRInputStream(filterString));
+			lexer.removeErrorListeners();
+			lexer.addErrorListener(ThrowingErrorListener.INSTANCE);
+
+			FilterGrammarParser parser = new FilterGrammarParser(new CommonTokenStream(lexer));
+			parser.removeErrorListeners();
+			parser.addErrorListener(ThrowingErrorListener.INSTANCE);
+
+			return new FilterVisitor(possibleEntityTypes).visit(parser.parse());
+		} catch (ParseCancellationException e) {
+			throw new IllegalArgumentException(
+					"Could not parse filter string '" + filterString + "'. Error: " + e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * Creates the value for the condition from the value given as string.
+	 * 
+	 * @param valueType
+	 *            The type that the value should have.
+	 * @param valueAsString
+	 *            The value as string.
+	 * @return The created value for the condition.
+	 * @throws IllegalArgumentException
+	 *             Thrown if the value type is not supported
+	 */
+	private static Object createConditionValue(ValueType<?> valueType, String valueAsString) {
+		Object ret = null;
+		if (ValueType.BOOLEAN.equals(valueType)) {
+			ret = Boolean.valueOf(valueAsString);
+		} else if (ValueType.LONG.equals(valueType)) {
+			ret = Long.valueOf(valueAsString);
+		} else if (ValueType.STRING.equals(valueType)) {
+			ret = valueAsString;
+		} else if (ValueType.BYTE.equals(valueType)) {
+			ret = Byte.valueOf(valueAsString);
+		} else if (ValueType.DOUBLE.equals(valueType)) {
+			ret = Double.valueOf(valueAsString);
+		} else if (ValueType.FLOAT.equals(valueType)) {
+			ret = Float.valueOf(valueAsString);
+		} else if (ValueType.INTEGER.equals(valueType)) {
+			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: " + valueType.toString());
+		}
+		return ret;
+	}
+
+	/**
+	 * Creates the values for the condition from the values given in the list of
+	 * strings.
+	 * 
+	 * @param valueType
+	 *            The type that the value should have.
+	 * @param valuesAsString
+	 *            The values as a list of string.
+	 * @return The created value for the condition.
+	 * @throws IllegalArgumentException
+	 *             Thrown if the value type is not supported
+	 */
+	private static Object createConditionValues(ValueType<?> valueType, List<String> valuesAsStrings) {
+
+		if (ValueType.BOOLEAN.equals(valueType)) {
+			List<Boolean> list = new ArrayList<>();
+			for (String valueAsString : valuesAsStrings) {
+				list.add(Boolean.valueOf(valueAsString));
+			}
+			return Booleans.toArray(list);
+		} else if (ValueType.LONG.equals(valueType)) {
+			List<Long> list = new ArrayList<>();
+			for (String valueAsString : valuesAsStrings) {
+				list.add(Long.valueOf(valueAsString));
+			}
+			return Longs.toArray(list);
+		} else if (ValueType.STRING.equals(valueType)) {
+			List<String> list = new ArrayList<>();
+			for (String valueAsString : valuesAsStrings) {
+				list.add(valueAsString);
+			}
+			return list.toArray(new String[0]);
+		} else if (ValueType.BYTE.equals(valueType)) {
+			List<Byte> list = new ArrayList<>();
+			for (String valueAsString : valuesAsStrings) {
+				list.add(Byte.valueOf(valueAsString));
+			}
+			return Bytes.toArray(list);
+		} else if (ValueType.DOUBLE.equals(valueType)) {
+			List<Double> list = new ArrayList<>();
+			for (String valueAsString : valuesAsStrings) {
+				list.add(Double.valueOf(valueAsString));
+			}
+			return Doubles.toArray(list);
+		} else if (ValueType.FLOAT.equals(valueType)) {
+			List<Float> list = new ArrayList<>();
+			for (String valueAsString : valuesAsStrings) {
+				list.add(Float.valueOf(valueAsString));
+			}
+			return Floats.toArray(list);
+		} else if (ValueType.INTEGER.equals(valueType)) {
+			List<Integer> list = new ArrayList<>();
+			for (String valueAsString : valuesAsStrings) {
+				list.add(Integer.valueOf(valueAsString));
+			}
+			return Ints.toArray(list);
+		} else if (ValueType.SHORT.equals(valueType)) {
+			List<Short> list = new ArrayList<>();
+			for (String valueAsString : valuesAsStrings) {
+				list.add(Short.valueOf(valueAsString));
+			}
+			return Shorts.toArray(list);
+		} else if (ValueType.DATE.equals(valueType)) {
+			List<LocalDateTime> list = new ArrayList<>();
+			for (String valueAsString : valuesAsStrings) {
+				try {
+					list.add(LocalDateTime.parse(valueAsString));
+				} catch (DateTimeParseException e) {
+					throw new IllegalArgumentException("Unsupported value for date: '" + valueAsString
+							+ "'. Expected format: '2007-12-03T10:15:30'");
+				}
+			}
+			return list.toArray(new LocalDateTime[0]);
+		} else {
+			throw new IllegalArgumentException("Unsupported value type: " + valueType.toString());
+		}
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/I18NActivity.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/I18NActivity.java
new file mode 100644
index 0000000..c45675d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/I18NActivity.java
@@ -0,0 +1,178 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.ServiceNotProvidedException;
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.adapter.ModelManager;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * I18NActivity Bean implementation to lookup localizations for business objects
+ * attributes and types
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class I18NActivity {
+
+	private static final Logger LOG = LoggerFactory.getLogger(I18NActivity.class);
+
+	private String RESOURCE_FILE = "org/eclipse/mdm/businessobjects/control/i18n/locale/localization";
+	private ResourceBundle LOCALIZATION_RESOURCES = ResourceBundle.getBundle(RESOURCE_FILE);
+
+	@Inject
+	private ConnectorService connectorService;
+
+	/**
+	 * localizes all attributes names from a type (e.g. TestStep.class)
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param type
+	 *            MDM business object type (e.g. TestStep.class)
+	 * @return a {@link Map} with localized {@link Attribute} names (key is
+	 *         attribute name, value is localized attribute name)
+	 */
+	public Map<Attribute, String> localizeAttributes(String sourceName, Class<? extends Entity> type) {
+
+		Map<Attribute, String> map = new HashMap<>();
+
+		ApplicationContext context = this.connectorService.getContextByName(sourceName);
+		
+		EntityType entityType = lookupEntityType(context, type);
+		localizeAttributes(entityType, map);
+
+		return map;
+	}
+
+	/**
+	 * localizes the type name of the given type (e.g. TestStep.class)
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param type
+	 *            type to localize its type name
+	 * @return the localizes type name (key is the original type name, value is
+	 *         the localized type name)
+	 */
+	public Map<EntityType, String> localizeType(String sourceName, Class<? extends Entity> type) {
+
+		Map<EntityType, String> map = new HashMap<>();
+
+		ApplicationContext context = this.connectorService.getContextByName(sourceName);
+		EntityType entityType = lookupEntityType(context, type);
+		localizeType(entityType, map);
+
+		return map;
+	}
+
+	/**
+	 * localizes all {@link Attribute}s of all types from the source identified
+	 * by the given soureName
+	 * 
+	 * @param sourceName
+	 *            source name to identify the MDM data source
+	 * @return a {@link Map} with localized {@link Attribute} names (key is
+	 *         attribute name, value is localized attribute name)
+	 */
+	public Map<Attribute, String> localizeAllAttributes(String sourceName) {
+
+		Map<Attribute, String> map = new HashMap<>();
+		ApplicationContext context = this.connectorService.getContextByName(sourceName);
+		List<EntityType> list = lookupAllEntityTypes(context);
+
+		for (EntityType entityType : list) {
+			localizeAttributes(entityType, map);
+		}
+
+		return map;
+	}
+
+	/**
+	 * localizes all types form the source identified by the given sourceName
+	 * 
+	 * @param sourceName
+	 *            source name to identify the MDM data source
+	 * @return the localizes type names (key is the original type name, value is
+	 *         the localized type name)
+	 */
+	public Map<EntityType, String> localizeAllTypes(String sourceName) {
+
+		Map<EntityType, String> map = new HashMap<>();
+		ApplicationContext context = this.connectorService.getContextByName(sourceName);
+		List<EntityType> list = lookupAllEntityTypes(context);
+
+		for (EntityType entityType : list) {
+			localizeType(entityType, map);
+		}
+
+		return map;
+	}
+
+	private void localizeType(EntityType entityType, Map<EntityType, String> map) {
+		String localization = localize(entityType.getName(), entityType.getName());
+		map.put(entityType, localization);
+	}
+
+	private void localizeAttributes(EntityType entityType, Map<Attribute, String> map) {
+
+		for (Attribute attribute : entityType.getAttributes()) {
+			String key = entityType.getName() + "." + attribute.getName();
+			String localization = localize(key, attribute.getName());
+			map.put(attribute, localization);
+		}
+
+	}
+
+	private EntityType lookupEntityType(ApplicationContext context, Class<? extends Entity> type) {
+		return context.getModelManager()
+			.map(mm -> mm.getEntityType(type))
+			.orElseThrow(() -> new ServiceNotProvidedException(ModelManager.class));
+	}
+
+	private List<EntityType> lookupAllEntityTypes(ApplicationContext context) {
+		return context.getModelManager()
+				.map(mm -> mm.listEntityTypes())
+				.orElseThrow(() -> new ServiceNotProvidedException(ModelManager.class));
+	}
+
+	private String localize(String key, String defaultValue) {
+		try {
+			return LOCALIZATION_RESOURCES.getString(key);
+		} catch (MissingResourceException e) {
+			LOG.debug("unable to localize key '" + key + "', no translation possible!");
+			return defaultValue;
+		}
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/MDMEntityAccessException.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/MDMEntityAccessException.java
new file mode 100644
index 0000000..f9e1e9c
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/MDMEntityAccessException.java
@@ -0,0 +1,30 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+public class MDMEntityAccessException extends RuntimeException {
+
+	private static final long serialVersionUID = -4196754501456211418L;
+
+	public MDMEntityAccessException(String message) {
+		super(message);
+	}
+
+	public MDMEntityAccessException(String message, Throwable t) {
+		super(message, t);
+	}
+
+}
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
new file mode 100644
index 0000000..d25d938
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/NavigationActivity.java
@@ -0,0 +1,218 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.model.Channel;
+import org.eclipse.mdm.api.base.model.ChannelGroup;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Measurement;
+import org.eclipse.mdm.api.base.model.Test;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.model.Pool;
+import org.eclipse.mdm.api.dflt.model.Project;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+
+/**
+ * NavigationActivity Bean implementation to lookup specified business object
+ * children
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class NavigationActivity {
+
+	@Inject
+	private ConnectorService connectorService;
+
+	// here for cdi to work
+	public NavigationActivity()  {
+
+	}
+
+	public NavigationActivity(ConnectorService connectorService) {
+		this.connectorService = requireNonNull(connectorService, "ConnectorService cannot be null!");
+	}
+
+	/**
+	 * returns the MDM {@link Environment} business objects of all connected MDM
+	 * systems
+	 * 
+	 * @return MDM {@link Environment} business objects
+	 */
+	public List<Environment> getEnvironments() {
+		try {
+			return this.connectorService.getContexts()
+					.stream()
+					.map(c -> c.getEntityManager())
+					.filter(Optional::isPresent)
+					.map(em -> em.get().loadEnvironment())
+					.collect(Collectors.toList());
+		} catch (DataAccessException e) {
+			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 {
+			return this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.map(em -> em.loadAll(Project.class))
+					.orElseThrow(() -> new MDMEntityAccessException("No EntityManager found for source " + sourceName + "!"));
+		} 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
+	 * 
+	 * @param sourceName
+	 *            Name of the MDM system
+	 * @return MDM {@link Test} business objects
+	 */
+	public List<Test> getTests(String sourceName) {
+		try {
+			return this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.map(em -> em.loadAll(Test.class))
+					.orElseThrow(() -> new MDMEntityAccessException("No EntityManager found for source " + sourceName + "!"));
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * 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, String 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, String projectID) {
+		return getChildren(sourceName, Project.class, projectID, Pool.class);
+	}
+
+	/**
+	 * returns all MDM {@link TestStep} business object children for a MDM
+	 * {@link Test} identified by the given source name and {@link Test} ID.
+	 * 
+	 * @param sourceName
+	 *            Name of the MDM system
+	 * @param testID
+	 *            The {@code Test} instance ID
+	 * @return MDM {@link TestStep} business objects
+	 */
+	public List<TestStep> getTestSteps(String sourceName, String testID) {
+		return getChildren(sourceName, Test.class, testID, TestStep.class);
+	}
+
+	/**
+	 * returns all MDM {@link Measurement} business object children for a MDM
+	 * {@link TestStep} identified by the given source name and {@link TestStep}
+	 * ID.
+	 * 
+	 * @param sourceName
+	 *            Name of the MDM system
+	 * @param testStepID
+	 *            The {@code TestStep} instance ID
+	 * @return MDM {@link Measurement} business objects
+	 */
+	public List<Measurement> getMeasurements(String sourceName, String testStepID) {
+		return getChildren(sourceName, TestStep.class, testStepID, Measurement.class);
+	}
+
+	/**
+	 * returns all MDM {@link ChannelGroup} business object children for a MDM
+	 * {@link Measurement} identified by the given source name and
+	 * {@link Measurement} ID.
+	 * 
+	 * @param sourceName
+	 *            Name of the MDM system
+	 * @param measurementID
+	 *            The {@code Measurement} instance ID
+	 * @return MDM {@link ChannelGroup} business objects
+	 */
+	public List<ChannelGroup> getChannelGroups(String sourceName, String measurementID) {
+		return getChildren(sourceName, Measurement.class, measurementID, ChannelGroup.class);
+	}
+
+	/**
+	 * returns all MDM {@link Channel} business object children for a MDM
+	 * {@link ChannelGroup} identified by the given source name and
+	 * {@link ChannelGroup} ID.
+	 * 
+	 * @param sourceName
+	 *            Name of the MDM system
+	 * @param channelGroupID
+	 *            The {@code ChannelGroup} instance ID
+	 * @return MDM {@link Channel} business objects
+	 */
+	public List<Channel> getChannels(String sourceName, String channelGroupID) {
+		return getChildren(sourceName, ChannelGroup.class, channelGroupID, Channel.class);
+	}
+
+	private <T extends Entity> List<T> getChildren(String sourceName, Class<? extends Entity> parentType, String parentID,
+			Class<T> childType) {
+		try {
+			return this.connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.map(em -> em.loadChildren(em.load(parentType, parentID), childType))
+					.orElseThrow(() -> new MDMEntityAccessException("No EntityManager found for source " + sourceName + "!"));
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+}
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
new file mode 100644
index 0000000..778d2e0
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchActivity.java
@@ -0,0 +1,159 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Resource;
+import javax.ejb.SessionContext;
+import javax.ejb.Stateless;
+import javax.mail.search.SearchException;
+
+import org.eclipse.mdm.api.base.ServiceNotProvidedException;
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.base.query.Filter;
+import org.eclipse.mdm.api.base.query.FilterItem;
+import org.eclipse.mdm.api.base.search.SearchService;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.eclipse.mdm.businessobjects.entity.SearchDefinition;
+import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
+
+/**
+ * SearchActivity Bean for searching business object and managing search
+ * definitions
+ *
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class SearchActivity {
+
+	@Resource
+	private SessionContext sessionContext;
+
+	/**
+	 * lists available global and user specific search definitions
+	 *
+	 * @return available global and user specific search definitions
+	 * @throws SearchException
+	 *             if an error occurs during lookup available search definitions
+	 */
+	public List<SearchDefinition> listSearchDefinitions() {
+		Principal principal = sessionContext.getCallerPrincipal();
+		SearchDefinitionReader scReader = new SearchDefinitionReader(principal);
+		return scReader.readSearchDefinitions();
+	}
+
+	/**
+	 * lists the available search attributes for the given result type.
+	 * 
+	 * @param resultType
+	 *            The result type.
+	 * @return The available search attributes.
+	 */
+	public <T extends Entity> List<SearchAttribute> listAvailableAttributes(ApplicationContext context, Class<T> resultType) {
+
+			SearchService searchService = context.getSearchService()
+					.orElseThrow(() -> new MDMEntityAccessException("SearchService not found!"));
+		
+		try {
+			List<SearchAttribute> searchAttributes = new ArrayList<>();
+			List<EntityType> entityTypes = searchService.listEntityTypes(resultType);
+
+			for (EntityType entityType : entityTypes) {
+				for (Attribute attr : entityType.getAttributes()) {
+					searchAttributes.add(new SearchAttribute(ServiceUtils.workaroundForTypeMapping(entityType),
+							attr.getName(), attr.getValueType().toString(), "*"));
+				}
+			}
+			return searchAttributes;
+			
+		} catch (IllegalArgumentException e) {
+			return new ArrayList<>();
+		}
+	}
+
+	/**
+	 * executes a search using the given filter and returns the search result
+	 *
+	 * @param resultType
+	 *            business object type of the search results
+	 * @param filterString
+	 *            filter for the search request
+	 * @return the found business objects
+	 */
+	public <T extends Entity> List<T> search(ApplicationContext context, Class<T> resultType, String filterString) {
+		try {
+			SearchService searchService = context.getSearchService()
+					.orElseThrow(() -> new ServiceNotProvidedException(SearchService.class));
+			List<EntityType> searchable = searchService.listEntityTypes(resultType);
+			Filter filter = FilterParser.parseFilterString(searchable, filterString);
+			List<Attribute> attributesList = getAttributeListFromFilter(filter);
+			return searchService.fetch(resultType, attributesList, filter);
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	private List<Attribute> getAttributeListFromFilter(Filter filter) {
+
+		List<Attribute> attributeList = new ArrayList<>();
+		Iterator<FilterItem> fIterator = filter.iterator();
+		while (fIterator.hasNext()) {
+			FilterItem filterItem = fIterator.next();
+			if (filterItem.isCondition()) {
+				attributeList.add(filterItem.getCondition().getAttribute());
+			}
+		}
+
+		return attributeList;
+	}
+
+	/**
+	 * executes a free textsearch
+	 *
+	 * @param query
+	 *            the query given to the search
+	 * @return the found business objects
+	 */
+	public List<Entity> search(ApplicationContext context, String query) {
+		try {
+			SearchService searchService = context.getSearchService()
+					.orElseThrow(() -> new ServiceNotProvidedException(SearchService.class));
+			List<Entity> allEntities = new ArrayList<>();
+
+			if (searchService.isTextSearchAvailable()) {
+				Map<Class<? extends Entity>, List<Entity>> fetch = searchService.fetch(query);
+				for (List<Entity> entities : fetch.values()) {
+					allEntities.addAll(entities);
+				}
+			}
+
+			return allEntities;
+		} catch (DataAccessException e) {
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchDefinitionReader.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchDefinitionReader.java
new file mode 100644
index 0000000..1d56b62
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchDefinitionReader.java
@@ -0,0 +1,217 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.eclipse.mdm.businessobjects.entity.SearchDefinition;
+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;
+
+/**
+ * SearchDefinitionReader class to read {@link SearchDefinition}s from an XML
+ * files
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class SearchDefinitionReader {
+
+	private static final String ROOT_ELEMENT_NAME = "mdm_search_definition";
+	private static final String ROOT_ELEMENT_ATTR_NAME = "name";
+	private static final String ROOT_ELEMENT_ATTR_RESULT = "resultType";
+	private static final String ATTRIBUTE_GROUP_ELEMENT = "attributes";
+	private static final String ATTRIBUTE_ELEMENT = "attribute";
+	private static final String ATTRIBUTE_ELEMENT_ATTR_TYPE = "boType";
+	private static final String ATTRIBUTE_ELEMENT_ATTR_VALUENAME = "attrName";
+	private static final String ATTRIBUTE_ELEMENT_ATTR_VALUETYPE = "valueType";
+	private static final String ATTRIBUTE_ELEMENT_ATTR_CRITERIA = "criteria";
+
+	private static final String COMPONENT_CONFIG_ROOT_FOLDER = "org.eclipse.mdm.search";
+
+	private final Principal principal;
+
+	private static final Logger LOG = LoggerFactory.getLogger(SearchDefinitionReader.class);
+
+	/**
+	 * Constructor
+	 * 
+	 * @param principal
+	 *            the current principal to load user specific search definition
+	 *            if available
+	 */
+	public SearchDefinitionReader(Principal principal) {
+		this.principal = principal;
+	}
+
+	/**
+	 * reads the {@link SearchDefinition}s from a available
+	 * {@link SearchDefinition} XML files
+	 * 
+	 * @return the read {@link SearchDefinition}s
+	 * 
+	 * @throws SearchException
+	 *             if an error occurs during reading the
+	 *             {@link SearchException}s from the XML files
+	 */
+	public List<SearchDefinition> readSearchDefinitions() {
+
+		List<SearchDefinition> searchDefinitionList = new ArrayList<>();
+		List<File> files = listSearchDefinitionFiles();
+
+		for (File file : files) {
+			searchDefinitionList.add(readSearchDefinitionFile(file));
+		}
+
+		return searchDefinitionList;
+	}
+
+	private SearchDefinition readSearchDefinitionFile(File file) {
+
+		InputStream is = null;
+
+		try {
+
+			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+			DocumentBuilder db = dbf.newDocumentBuilder();
+
+			is = new BufferedInputStream(new FileInputStream(file));
+			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 XMLFormatException(message);
+			}
+
+			String name = readElementAttribute(ROOT_ELEMENT_ATTR_NAME, "", true, root);
+			String resultType = readElementAttribute(ROOT_ELEMENT_ATTR_RESULT, "", true, root);
+
+			SearchDefinition searchDefinition = new SearchDefinition(name, resultType);
+
+			Element[] attributeGroups = getChildElementsByName(root, ATTRIBUTE_GROUP_ELEMENT, true);
+			for (Element attributeGroup : attributeGroups) {
+				Element[] attributes = getChildElementsByName(attributeGroup, ATTRIBUTE_ELEMENT, true);
+				for (Element attribute : attributes) {
+					String boType = readElementAttribute(ATTRIBUTE_ELEMENT_ATTR_TYPE, "", true, attribute);
+					String attrName = readElementAttribute(ATTRIBUTE_ELEMENT_ATTR_VALUENAME, "", true, attribute);
+					String valueType = readElementAttribute(ATTRIBUTE_ELEMENT_ATTR_VALUETYPE, "", true, attribute);
+					String criteria = readElementAttribute(ATTRIBUTE_ELEMENT_ATTR_CRITERIA, "*", false, attribute);
+					searchDefinition.addSearchAttribute(boType, attrName, valueType, criteria);
+				}
+			}
+			return searchDefinition;
+
+		} catch (ParserConfigurationException | SAXException | IOException e) {
+			throw new XMLParseException(e.getMessage(), e);
+		} finally {
+			closeInputStream(is);
+		}
+	}
+
+	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 message = "mandatory element '" + name + "' not found!";
+			throw new XMLFormatException(message);
+		}
+
+		return elements.toArray(new Element[elements.size()]);
+	}
+
+	private String readElementAttribute(String attrName, String defaultValue, boolean mandatory, Element element) {
+
+		String value = element.getAttribute(attrName);
+		if (value.trim().length() <= 0) {
+			if (mandatory) {
+				String elementName = element.getNodeName();
+				String message = "mandatory attribute '" + attrName + "' at element '" + elementName + "' is missing!";
+				throw new XMLFormatException(message);
+			}
+			value = defaultValue;
+		}
+		return value;
+	}
+
+	private List<File> listSearchDefinitionFiles() {
+
+		List<File> files = new ArrayList<>();
+
+		File globalFolder = new File(COMPONENT_CONFIG_ROOT_FOLDER);
+		if (!globalFolder.exists() || !globalFolder.isDirectory()) {
+			return Collections.emptyList();
+		}
+		files.addAll(listSearchDefinitionFiles(globalFolder));
+
+		File userFolder = new File(globalFolder, this.principal.getName());
+		if (userFolder.exists() && userFolder.isDirectory()) {
+			files.addAll(listSearchDefinitionFiles(userFolder));
+		}
+
+		return files;
+	}
+
+	private List<File> listSearchDefinitionFiles(File rootFolder) {
+		List<File> list = new ArrayList<>();
+		File[] files = rootFolder.listFiles();
+		for (File file : files) {
+			if (file.isDirectory()) {
+				continue;
+			}
+			if (file.getName().toLowerCase().endsWith(".xml")) {
+				list.add(file);
+			}
+		}
+		return list;
+	}
+
+	private void closeInputStream(InputStream is) {
+		try {
+			if (is != null) {
+				is.close();
+			}
+		} catch (IOException e) {
+			LOG.error(e.getMessage());
+		}
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchFilterBuilder.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchFilterBuilder.java
new file mode 100644
index 0000000..fb6c2d0
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/SearchFilterBuilder.java
@@ -0,0 +1,113 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.mdm.api.base.query.Condition;
+import org.eclipse.mdm.api.base.query.Filter;
+
+/**
+ * Filter builder.
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class SearchFilterBuilder {
+
+	private static final String AND = "and";
+	private static final String OR = "or";
+
+	private List<Object> filterElements = new ArrayList<>();
+	private List<Filter> filterList = new ArrayList<>();
+	private String currentFilterType = null;
+	private Filter currentFilter = null;
+
+	/**
+	 * Adds a condition
+	 * 
+	 * @param condition
+	 */
+	public void addCondition(Condition condition) {
+		filterElements.add(condition);
+	}
+
+	/**
+	 * Adds a and operator.
+	 */
+	public void addAnd() {
+		filterElements.add(AND);
+	}
+
+	/**
+	 * Adds an or operator.
+	 */
+	public void addOr() {
+		filterElements.add(OR);
+	}
+
+	/**
+	 * Creates the filter.
+	 * 
+	 * @return The created filter.
+	 */
+	public Filter build() {
+		for (int i = 0; i < filterElements.size(); i++) {
+			if (filterElements.get(i) instanceof Condition) {
+				if (currentFilter == null) {
+					createNextFilter(i);
+				}
+				if (filterElements.size() > (i + 1) && AND.equals(currentFilterType)
+						&& OR.equals(filterElements.get(i + 1))) {
+					filterList.add(currentFilter);
+					currentFilter = Filter.or();
+					currentFilterType = OR;
+				}
+				currentFilter.add((Condition) filterElements.get(i));
+			} else {
+				if (currentFilterType != filterElements.get(i)) {
+					filterList.add(currentFilter);
+					currentFilterType = (String) filterElements.get(i);
+					currentFilter = null;
+				}
+			}
+		}
+		if (currentFilter != null) {
+			filterList.add(currentFilter);
+		}
+		return Filter.and().merge(filterList);
+	}
+
+	/**
+	 * Create the next filter.
+	 * 
+	 * @param index
+	 *            The current filter element index.
+	 * @return The created filter.
+	 */
+	private Filter createNextFilter(int index) {
+		if (filterElements.size() > (index + 1)) {
+			currentFilter = AND.equals((String) filterElements.get(index + 1)) ? Filter.and() : Filter.or();
+			currentFilterType = (String) filterElements.get(index + 1);
+		} else if (AND.equals(currentFilterType)) {
+			currentFilter = Filter.and();
+		} else {
+			currentFilter = Filter.or();
+		}
+		return null;
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/XMLFormatException.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/XMLFormatException.java
new file mode 100644
index 0000000..48c39d3
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/XMLFormatException.java
@@ -0,0 +1,30 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+public class XMLFormatException extends RuntimeException {
+
+	private static final long serialVersionUID = -4196754501456211418L;
+
+	public XMLFormatException(String message) {
+		super(message);
+	}
+
+	public XMLFormatException(String message, Throwable t) {
+		super(message, t);
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/XMLParseException.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/XMLParseException.java
new file mode 100644
index 0000000..3defa48
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/control/XMLParseException.java
@@ -0,0 +1,30 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control;
+
+public class XMLParseException extends RuntimeException {
+
+	private static final long serialVersionUID = -4196754501456211418L;
+
+	public XMLParseException(String message) {
+		super(message);
+	}
+
+	public XMLParseException(String message, Throwable t) {
+		super(message, t);
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextCollection.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextCollection.java
new file mode 100644
index 0000000..560349c
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextCollection.java
@@ -0,0 +1,82 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.mdm.api.base.model.ContextComponent;
+import org.eclipse.mdm.api.base.model.ContextRoot;
+import org.eclipse.mdm.api.base.model.ContextType;
+
+/**
+ * ContextCollection (Entity for context data (ordered and measured))
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class ContextCollection {
+
+	public Map<ContextType, List<MDMEntity>> contextMeasured = new HashMap<>();
+	public Map<ContextType, List<MDMEntity>> contextOrdered = new HashMap<>();
+
+	/**
+	 * set the measured context data map
+	 * 
+	 * @param contextMap
+	 *            the measured context data map
+	 */
+	public void setMeasuredContext(Map<ContextType, ContextRoot> contextMap) {
+
+		for (java.util.Map.Entry<ContextType, ContextRoot> setEntry : contextMap.entrySet()) {
+
+			ContextType contextType = setEntry.getKey();
+			ContextRoot contextRoot = setEntry.getValue();
+
+			this.contextMeasured.put(contextType, new ArrayList<>());
+
+			for (ContextComponent contextComponent : contextRoot.getContextComponents()) {
+				MDMEntity entity = new MDMEntity(contextComponent);
+				this.contextMeasured.get(contextType).add(entity);
+			}
+		}
+	}
+
+	/**
+	 * sets the ordered context data map
+	 * 
+	 * @param contextMap
+	 *            the ordered context data map
+	 */
+	public void setOrderedContext(Map<ContextType, ContextRoot> contextMap) {
+
+		for (java.util.Map.Entry<ContextType, ContextRoot> setEntry : contextMap.entrySet()) {
+
+			ContextType contextType = setEntry.getKey();
+			ContextRoot contextRoot = setEntry.getValue();
+
+			this.contextOrdered.put(contextType, new ArrayList<>());
+
+			for (ContextComponent contextComponent : contextRoot.getContextComponents()) {
+				MDMEntity entity = new MDMEntity(contextComponent);
+				this.contextOrdered.get(contextType).add(entity);
+			}
+		}
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextResponse.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextResponse.java
new file mode 100644
index 0000000..abca6b6
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextResponse.java
@@ -0,0 +1,60 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.eclipse.mdm.api.base.model.ContextRoot;
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.control.ContextActivity;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ContextResponse {
+
+	/** transferable data content */
+	private List<ContextCollection> data;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param contextMap
+	 *            map with context data (ordered and measured)
+	 */
+	public ContextResponse(Map<String, Map<ContextType, ContextRoot>> contextMap) {
+		this.data = new ArrayList<>();
+		ContextCollection contextData = new ContextCollection();
+		contextData.setOrderedContext(contextMap.get(ContextActivity.CONTEXT_GROUP_ORDERED));
+		contextData.setMeasuredContext(contextMap.get(ContextActivity.CONTEXT_GROUP_MEASURED));
+		this.data.add(contextData);
+	}
+
+	/**
+	 * returns the context data
+	 * 
+	 * @return the context data
+	 */
+	public List<ContextCollection> getData() {
+		return Collections.unmodifiableList(this.data);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextSensorCollection.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextSensorCollection.java
new file mode 100644
index 0000000..65b0aa3
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextSensorCollection.java
@@ -0,0 +1,62 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.mdm.api.base.model.ContextSensor;
+
+/**
+ * ContextSensorCollection (Entity for context data (ordered and measured))
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class ContextSensorCollection {
+
+	public List<MDMEntity> sensorContextMeasured = new ArrayList<MDMEntity>();
+	public List<MDMEntity> sensorContextOrdered = new ArrayList<MDMEntity>();
+
+	/**
+	 * set the measured context sensor data list
+	 * 
+	 * @param sensorList
+	 *            the measured context sensor data list
+	 */
+	public void setMeasuredSensorContext(List<ContextSensor> sensorList) {
+
+		for (ContextSensor contextSensor : sensorList) {
+			MDMEntity entity = new MDMEntity(contextSensor);
+			this.sensorContextMeasured.add(entity);
+		}
+	}
+
+	/**
+	 * set the ordered context sensor data list
+	 * 
+	 * @param sensorList
+	 *            the ordered context sensor data list
+	 */
+	public void setOrderedSensorContext(List<ContextSensor> sensorList) {
+
+		for (ContextSensor contextSensor : sensorList) {
+			MDMEntity entity = new MDMEntity(contextSensor);
+			this.sensorContextOrdered.add(entity);
+		}
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextSensorResponse.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextSensorResponse.java
new file mode 100644
index 0000000..f2ca6da
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/ContextSensorResponse.java
@@ -0,0 +1,63 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.eclipse.mdm.api.base.model.ContextSensor;
+import org.eclipse.mdm.businessobjects.control.ContextActivity;
+
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ContextSensorResponse {
+
+	/** transferable data content */
+	private List<ContextSensorCollection> data;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param contextMap
+	 *            map with context data (ordered and measured)
+	 */
+	public ContextSensorResponse(Map<String, List<ContextSensor>> sensorMap) {
+		this.data = new ArrayList<>();
+		ContextSensorCollection contextSensorData = new ContextSensorCollection();
+		if (sensorMap.containsKey(ContextActivity.CONTEXT_SENSOR_GROUP_ORDERED)) {
+			contextSensorData.setOrderedSensorContext(sensorMap.get(ContextActivity.CONTEXT_SENSOR_GROUP_ORDERED));
+		}
+		if (sensorMap.containsKey(ContextActivity.CONTEXT_SENSOR_GROUP_MEASURED)) {
+			contextSensorData.setMeasuredSensorContext(sensorMap.get(ContextActivity.CONTEXT_SENSOR_GROUP_MEASURED));
+		}
+		this.data.add(contextSensorData);
+	}
+
+	/**
+	 * returns the {@link ContextSensor} data
+	 * 
+	 * @return the {@link ContextSensor} data
+	 */
+	public List<ContextSensorCollection> getData() {
+		return Collections.unmodifiableList(this.data);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/I18NLocalization.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/I18NLocalization.java
new file mode 100644
index 0000000..af957bc
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/I18NLocalization.java
@@ -0,0 +1,52 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+/**
+ * ContextData (Entity for I18N localizations (with original name and localized
+ * name)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class I18NLocalization {
+
+	/** name of the attribute */
+	private final String name;
+	/** localized name of the attribute */
+	private final String localizedName;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param name
+	 *            name of the attribute
+	 * @param localizedName
+	 *            name of the localized attribute
+	 */
+	public I18NLocalization(String name, String localizedName) {
+		this.name = name;
+		this.localizedName = localizedName;
+	}
+
+	public String getName() {
+		return this.name;
+	}
+
+	public String getLocalizedName() {
+		return this.localizedName;
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/I18NResponse.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/I18NResponse.java
new file mode 100644
index 0000000..9b835ea
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/I18NResponse.java
@@ -0,0 +1,87 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+
+import java.util.Set;
+
+/**
+ * I18NResponse (Container for {@link I18NLocalization}s)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class I18NResponse {
+
+	/** transferable data content */
+	private final List<I18NLocalization> data;
+
+	/**
+	 * 
+	 * @param localizedEntityTypeMap
+	 * @param localizedAttributeMap
+	 */
+	public I18NResponse(Map<EntityType, String> localizedEntityTypeMap, Map<Attribute, String> localizedAttributeMap) {
+		this.data = toTransferable(localizedEntityTypeMap, localizedAttributeMap);
+	}
+
+	public List<I18NLocalization> getData() {
+		return Collections.unmodifiableList(this.data);
+	}
+
+	private List<I18NLocalization> toTransferable(Map<EntityType, String> localizedEntityTypeMap,
+			Map<Attribute, String> localizedAttributeMap) {
+
+		List<I18NLocalization> localizationList = new ArrayList<>();
+
+		Set<Entry<EntityType, String>> entityTypeSet = localizedEntityTypeMap.entrySet();
+
+		for (Entry<EntityType, String> entry : entityTypeSet) {
+
+			EntityType entityType = entry.getKey();
+			String key = entityType.getName();
+
+			localizationList.add(new I18NLocalization(key, entry.getValue()));
+		}
+
+		Set<Entry<Attribute, String>> attributeSet = localizedAttributeMap.entrySet();
+
+		for (Entry<Attribute, String> entry : attributeSet) {
+
+			Attribute attribute = entry.getKey();
+			EntityType entityType = attribute.getEntityType();
+			String key = entityType.getName() + "." + attribute.getName();
+
+			localizationList.add(new I18NLocalization(key, entry.getValue()));
+		}
+
+		return localizationList;
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMAttribute.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMAttribute.java
new file mode 100644
index 0000000..1f43bdc
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMAttribute.java
@@ -0,0 +1,69 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+/**
+ * Attribute (Entity for attribute informations)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class MDMAttribute {
+
+	/** name of the attribute value */
+	private final String name;
+	/** string value of the attribute value */
+	private final String value;
+	/** unit of the attribute value */
+	private final String unit;
+	/** data type of the attribute value */
+	private final String dataType;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param name
+	 *            name of the attribute value
+	 * @param value
+	 *            string value of the attribute value
+	 * @param unit
+	 *            unit of the attribute value
+	 * @param dataType
+	 *            data type of the attribute value
+	 */
+	public MDMAttribute(String name, String value, String unit, String dataType) {
+		this.name = name;
+		this.value = value;
+		this.unit = unit;
+		this.dataType = dataType;
+	}
+
+	public String getName() {
+		return this.name;
+	}
+
+	public String getValue() {
+		return this.value;
+	}
+
+	public String getUnit() {
+		return this.unit;
+	}
+
+	public String getDataType() {
+		return this.dataType;
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntity.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntity.java
new file mode 100644
index 0000000..f72001f
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntity.java
@@ -0,0 +1,182 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.mdm.api.base.model.BaseEntity;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.Value;
+
+/**
+ * MDMEntity (Entity for a business object (contains a list of
+ * {@link MDMAttribute}s)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class MDMEntity {
+
+	/** name of the MDM business object */
+	private final String name;
+	/** id of the MDM business object */
+	private final String id;
+	/** type as String of the MDM business object (e.g. TestStep) */
+	private final String type;
+	/** source type name of the business object at the data source */
+	private final String sourceType;
+	/** source name (e.g. MDM Environment name) */
+	private final String sourceName;
+	/** list of attribute to transfer */
+	private List<MDMAttribute> attributes;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param entity
+	 *            the business object
+	 */
+	public MDMEntity(Entity entity) {
+		this.name = entity.getName();
+		this.id = entity.getID();
+		this.type = entity.getClass().getSimpleName();
+		this.sourceType = entity.getTypeName();
+		this.sourceName = entity.getSourceName();
+		this.attributes = convertAttributeValues(entity.getValues());
+	}
+
+	public String getName() {
+		return this.name;
+	}
+
+	public String getId() {
+		return this.id;
+	}
+
+	public String getType() {
+		return this.type;
+	}
+
+	public String getSourceType() {
+		return this.sourceType;
+	}
+
+	public String getSourceName() {
+		return this.sourceName;
+	}
+
+	public List<MDMAttribute> getAttributes() {
+		return Collections.unmodifiableList(this.attributes);
+	}
+
+	/**
+	 * converts the MDM business object values to string values
+	 * 
+	 * @param values
+	 *            values of a MDM business object
+	 * @return list with converted attribute values
+	 */
+	private List<MDMAttribute> convertAttributeValues(Map<String, Value> values) {
+		List<MDMAttribute> listAttrs = new ArrayList<>();
+		Set<java.util.Map.Entry<String, Value>> set = values.entrySet();
+
+		for (java.util.Map.Entry<String, Value> entry : set) {
+
+			if (entry.getKey().equals(BaseEntity.ATTR_ID)) {
+				continue;
+			}
+
+			if (!entry.getValue().isValid()) {
+				String dt = entry.getValue().getValueType().toString();
+				listAttrs.add(new MDMAttribute(entry.getKey(), "", "", dt));
+				continue;
+			}
+
+			if (entry.getValue().getValueType().isSequence()) {
+				listAttrs.add(sequenceType2Attribute(entry.getKey(), entry.getValue()));
+			} else {
+				listAttrs.add(singleType2Attribute(entry.getKey(), entry.getValue()));
+			}
+		}
+		
+		return listAttrs;
+	}
+
+	/**
+	 * converts a single type MDM business object value to a attribute
+	 * 
+	 * @param name
+	 *            name of the attribute value
+	 * @param singleValue
+	 *            single MDM business object value
+	 * @return the converted attribute value
+	 */
+	private MDMAttribute singleType2Attribute(String name, Value singleValue) {
+		String value = singleValue.extract().toString();
+		String unit = singleValue.getUnit();
+		String dt = singleValue.getValueType().toString();
+		return new MDMAttribute(name, value, unit, dt);
+	}
+
+	/**
+	 * converts a sequence type MDM business object value to a attribute
+	 * 
+	 * @param name
+	 *            name of the attribute value
+	 * @param sequenceValue
+	 *            sequence MDM business object value
+	 * @return the converted attribute value
+	 */
+	private MDMAttribute sequenceType2Attribute(String name, Value sequenceValue) {
+
+		if (sequenceValue.getValueType().isStringSequence()) {
+			return stringSeq2Attribute(name, sequenceValue);
+		}
+
+		String dt = sequenceValue.getValueType().toString();
+		String defValue = "sequence type '" + dt + "' not implemented yet";
+		return new MDMAttribute(name, defValue, "", dt);
+	}
+
+	/**
+	 * converts a string sequence MDM business object value to a attribute The
+	 * result is a separated string (separator: ';')
+	 * 
+	 * @param name
+	 *            name of the attribute value
+	 * @param value
+	 *            string sequence MDM business object value
+	 * @return the converted attribute value
+	 */
+	private MDMAttribute stringSeq2Attribute(String name, Value value) {
+		String[] stringSeq = value.extract();
+		StringBuffer sb = new StringBuffer();
+
+		for (String stringSeqValue : stringSeq) {
+			sb.append(";").append(stringSeqValue);
+		}
+
+		String stringValue = sb.toString().replaceFirst(";", "");
+		String unit = value.getUnit();
+		String dt = value.getValueType().toString();
+		return new MDMAttribute(name, stringValue, unit, dt);
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntityResponse.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntityResponse.java
new file mode 100644
index 0000000..b822775
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntityResponse.java
@@ -0,0 +1,89 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.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;
+
+import org.eclipse.mdm.api.base.model.Entity;
+
+/**
+ * EntryResponse (Container for {@link MDMEntity}s)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class MDMEntityResponse {
+
+	/** type of all content entries (e.g. TestStep) */
+	private final String type;
+	/** transferable data content */
+	private final List<MDMEntity> data;
+
+	/**
+	 * Constructor (for a list of business objects {@link MDMEntity}s)
+	 * 
+	 * @param type
+	 *            type of all containging {@link MDMEntity}s
+	 * @param entries
+	 *            list of {@link MDMEntity}
+	 */
+	// TODO move to Vavr List
+	public <T extends Entity> MDMEntityResponse(Class<? extends Entity> type, List<T> businessObjects) {
+		this.type = type.getSimpleName();
+		this.data = toTransferable(businessObjects);
+	}
+
+	/**
+	 * Constructor (for a single business object {@link MDMEntity})
+	 * 
+	 * @param type
+	 *            type of the {@link MDMEntity}
+	 * @param businessObject
+	 *            single {@link MDMEntity}
+	 */
+	public <T extends Entity> MDMEntityResponse(Class<? extends Entity> type, T businessObject) {
+		List<T> businessObjects = new ArrayList<>();
+		businessObjects.add(businessObject);
+		this.type = type.getSimpleName();
+		this.data = toTransferable(businessObjects);
+	}
+
+	public String getType() {
+		return this.type;
+	}
+
+	public List<MDMEntity> getData() {
+		return Collections.unmodifiableList(this.data);
+	}
+
+	private <T extends Entity> List<MDMEntity> toTransferable(List<T> businessObjects) {
+		List<MDMEntity> mdmEntityList = new ArrayList<MDMEntity>();
+		for (Entity businessObject : businessObjects) {
+			MDMEntity mdmEntity = new MDMEntity(businessObject);
+			mdmEntityList.add(mdmEntity);
+		}
+		return mdmEntityList;
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchAttribute.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchAttribute.java
new file mode 100644
index 0000000..aac5610
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchAttribute.java
@@ -0,0 +1,87 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+/**
+ * SearchAttibute (Entity for {@link SearchAttribute}s defined at a
+ * {@link SearchDefinition})
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class SearchAttribute {
+
+	private final String boType;
+	private final String attrName;
+	private final String valueType;
+	private final String criteria;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param boType
+	 *            name of MDM business object (e.g. TestStep)
+	 * @param attrName
+	 *            name of an attribute for the MDM business object (e.g. Name)
+	 * @param valueType
+	 *            value type of this attribute (e.g. String)
+	 * @param criteria
+	 *            default criteria
+	 */
+	public SearchAttribute(String boType, String attrName, String valueType, String criteria) {
+		this.boType = boType;
+		this.attrName = attrName;
+		this.valueType = valueType;
+		this.criteria = criteria;
+	}
+
+	/**
+	 * returns the name of the MDM business object (e.g. TestStep)
+	 * 
+	 * @return the name of the MDM business object (e.g. TestStep)
+	 */
+	public String getBoType() {
+		return this.boType;
+	}
+
+	/**
+	 * returns the name of an attribute for the MDM business object (e.g. Name)
+	 * 
+	 * @return the name of an attribute for the MDM business object (e.g. Name)
+	 */
+	public String getAttrName() {
+		return this.attrName;
+	}
+
+	/**
+	 * returns the value type of this attribute (e.g. String)
+	 * 
+	 * @return the value type of this attribute (e.g. String)
+	 */
+	public String getValueType() {
+		return this.valueType;
+	}
+
+	/**
+	 * returns the default criteria
+	 * 
+	 * @return the default criteria
+	 */
+	public String getCriteria() {
+		return this.criteria;
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchAttributeResponse.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchAttributeResponse.java
new file mode 100644
index 0000000..15db426
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchAttributeResponse.java
@@ -0,0 +1,60 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.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;
+
+/**
+ * SearchDefinitionResponse (Container for {@link SearchAttribute}s)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class SearchAttributeResponse {
+
+	/** transferable data content */
+	private List<SearchAttribute> data;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param searchAttributes
+	 *            list of {@link SearchAttribute}s to transfer
+	 */
+	public SearchAttributeResponse(List<SearchAttribute> searchAttributes) {
+		data = new ArrayList<>(searchAttributes);
+	}
+
+	/**
+	 * Constructor
+	 */
+	public SearchAttributeResponse() {
+		data = new ArrayList<>();
+	}
+
+	public List<SearchAttribute> getData() {
+		return Collections.unmodifiableList(this.data);
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchDefinition.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchDefinition.java
new file mode 100644
index 0000000..9f6ce66
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchDefinition.java
@@ -0,0 +1,91 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.entity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * SearchDefinition (Entity with a list of defined {@link SearchAttribute}s)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class SearchDefinition {
+
+	private final String name;
+	private final String resultType;
+	private List<SearchAttribute> attributeList;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param name
+	 *            name of this search definition
+	 * @param resultType
+	 *            result type by executing this defined search (e.g. TestStep)
+	 */
+	public SearchDefinition(String name, String resultType) {
+		this.name = name;
+		this.resultType = resultType;
+		this.attributeList = new ArrayList<>();
+	}
+
+	/**
+	 * adds an {@link SearchAttribute} to this SearchDefinition
+	 * 
+	 * @param boType
+	 *            name of MDM business object (e.g. TestStep)
+	 * @param attrName
+	 *            name of an attribute for the MDM business object (e.g. Name)
+	 * @param valueType
+	 *            value type of this attribute (e.g. String)
+	 * @param criteria
+	 *            default criteria
+	 */
+	public void addSearchAttribute(String boType, String attrName, String valueType, String criteria) {
+		this.attributeList.add(new SearchAttribute(boType, attrName, valueType, criteria));
+	}
+
+	/**
+	 * returns the name of this search definition
+	 * 
+	 * @return the name of this search definition
+	 */
+	public String getName() {
+		return this.name;
+	}
+
+	/**
+	 * returns the result type of this search definition
+	 * 
+	 * @return the result type of this search definition
+	 */
+	public String getResultType() {
+		return this.resultType;
+	}
+
+	/**
+	 * lists the defined {@link SearchAttribute}s for this search definition
+	 * 
+	 * @return the defined {@link SearchAttribute}s for ths search definition
+	 */
+	public List<SearchAttribute> listSearchAttributes() {
+		return Collections.unmodifiableList(this.attributeList);
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchDefinitionResponse.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchDefinitionResponse.java
new file mode 100644
index 0000000..4c1bf61
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/SearchDefinitionResponse.java
@@ -0,0 +1,59 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.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;
+
+/**
+ * SearchDefinitionResponse (Container for {@link SearchDefinition}s)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class SearchDefinitionResponse {
+
+	/** transferable data content */
+	private List<SearchDefinition> data;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param searchDefinitions
+	 *            list of {@link SearchDefinition}s to transfer
+	 */
+	public SearchDefinitionResponse(List<SearchDefinition> searchDefinitions) {
+		this.data = new ArrayList<>(searchDefinitions);
+	}
+
+	/**
+	 * Constructor
+	 */
+	public SearchDefinitionResponse() {
+		this.data = new ArrayList<>();
+	}
+
+	public List<SearchDefinition> getData() {
+		return Collections.unmodifiableList(this.data);
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/DataAccessHelper.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/DataAccessHelper.java
new file mode 100644
index 0000000..936455a
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/DataAccessHelper.java
@@ -0,0 +1,132 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.service;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+import org.eclipse.mdm.api.base.Transaction;
+import org.eclipse.mdm.api.base.model.Deletable;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.businessobjects.control.MDMEntityAccessException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vavr.CheckedFunction2;
+
+/**
+ * Helper class providing functions to realize transactional data operations
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+public final class DataAccessHelper {
+
+	private static final Logger LOG = LoggerFactory.getLogger(DataAccessHelper.class);
+
+	/**
+	 * Just hide the default constructor
+	 */
+	private DataAccessHelper() {
+	}
+
+	/**
+	 * Returns a function that executes one transactional operation within a
+	 * transaction.
+	 * 
+	 * @return Function that executes a transactional operation on an entity within
+	 *         a transaction.
+	 */
+	// TODO anehmer on 2017-11-22: extend this method to handle lists of objects
+	public static <T extends Entity> T execute(EntityManager em, T entity,
+			CheckedFunction2<Transaction, Entity, Entity> operation) {
+		Transaction t = null;
+		try {
+			// start transaction to persist ValueList
+			t = em.startTransaction();
+			// perform the transactional operation
+			@SuppressWarnings("unchecked")
+			T processedEntity = (T) operation.apply(t, entity);
+			// commit the transaction
+			t.commit();
+			// return the processed entity
+			return processedEntity;
+		} catch (Throwable e) {
+			if (t != null) {
+				t.abort();
+			}
+			throw new MDMEntityAccessException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * Function that creates an {@link Entity} and returns it.
+	 */
+	public static final CheckedFunction2<Transaction, Entity, Entity> DELETE = (transaction,
+			entity) -> {
+		if (entity instanceof Deletable) {
+			// TODO anehmer on 2017-11-22: call to delete() should return entity
+			transaction.delete(Arrays.asList((Deletable) entity));
+		}
+		// if entity cannot be deleted
+		else {
+			throw new MDMEntityAccessException("Entity to delete is no Deletable");
+		}
+		return entity;
+	};
+
+	/**
+	 * Function that creates the given {@link Entity} and returns the updated
+	 * entity.
+	 */
+	public static final CheckedFunction2<Transaction, Entity, Entity> CREATE = (transaction,
+			entity) -> {
+		// TODO anehmer on 2017-11-22: call to create() should return entity
+		transaction.create(Arrays.asList(entity));
+		return entity;
+	};
+
+	/**
+	 * Function that updates the given {@link Entity} it is executed upon and
+	 * returns the updated entity.
+	 */
+	public static final CheckedFunction2<Transaction, Entity, Entity> UPDATE = (transaction,
+			entity) -> {
+		// TODO anehmer on 2017-11-22: call to update() should return entity
+		transaction.update(Arrays.asList(entity));
+		return entity;
+	};
+
+	/**
+	 * Handles a {@link Throwable} by loggging the exception message and rethrowing
+	 * a {@link MDMEntityAccessException}
+	 */
+	// TODO should be replaced in Resources by buildErrorResponse()
+	public static final Consumer<? super Throwable> rethrowAsMDMEntityAccessException = e -> {
+		// TODO anehmer on 2017-11-09: check if logging is necessary depending on how we
+		// handle error logging and client response in general
+		LOG.error(e.getMessage(), e);
+		throw new MDMEntityAccessException(e.getMessage(), e);
+	};
+
+	/**
+	 * Function that handles an occurred exception without rethrowing it
+	 */
+	// TODO anehmer on 2017-11-22: remove method and logger
+	public static final Consumer<? super Throwable> handleException = e -> {
+		LOG.error(e.getMessage(), e);
+	};
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/EntityService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/EntityService.java
new file mode 100644
index 0000000..155407c
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/EntityService.java
@@ -0,0 +1,767 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.service;
+
+import static io.vavr.API.Tuple;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;

+import javax.inject.Inject;

+

+import org.apache.commons.lang3.NotImplementedException;
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.Core;
+import org.eclipse.mdm.api.base.adapter.EntityStore;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.BaseEntity;
+import org.eclipse.mdm.api.base.model.ContextComponent;
+import org.eclipse.mdm.api.base.model.ContextRoot;
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.EnumRegistry;
+import org.eclipse.mdm.api.base.model.EnumerationValue;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.api.dflt.model.CatalogAttribute;
+import org.eclipse.mdm.api.dflt.model.CatalogComponent;
+import org.eclipse.mdm.api.dflt.model.CatalogSensor;
+import org.eclipse.mdm.api.dflt.model.EntityFactory;
+import org.eclipse.mdm.api.dflt.model.TemplateAttribute;
+import org.eclipse.mdm.api.dflt.model.TemplateComponent;
+import org.eclipse.mdm.api.dflt.model.TemplateRoot;
+import org.eclipse.mdm.api.dflt.model.TemplateSensor;
+import org.eclipse.mdm.api.dflt.model.TemplateTest;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStepUsage;
+import org.eclipse.mdm.api.dflt.model.ValueList;
+import org.eclipse.mdm.api.dflt.model.ValueListValue;
+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.EntityNotFoundException;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.vavr.CheckedFunction0;
+import io.vavr.Function0;
+import io.vavr.Tuple;
+import io.vavr.Tuple2;
+import io.vavr.Value;
+import io.vavr.collection.HashMap;
+import io.vavr.collection.List;
+import io.vavr.collection.Map;
+import io.vavr.collection.Seq;
+import io.vavr.collection.Set;
+import io.vavr.collection.Stream;
+import io.vavr.control.Option;
+import io.vavr.control.Try;
+
+/**
+ * Class providing basic data access methods to {@link Entity}s.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+@Stateless
+public class EntityService {
+
+	@Inject
+	private ConnectorService connectorService;
+
+	@EJB
+	private SearchActivity searchActivity;
+
+	@EJB
+	private NavigationActivity navigationActivity;
+
+	@EJB
+	private I18NActivity i18nActivity;
+
+	private static final Logger LOGGER = LoggerFactory.getLogger(EntityService.class);
+
+	/**
+	 * Converts a {@code value} into a {@link Value}. If {@code value} is
+	 * {@code null}, {@code Value.isEmpty() == true}.
+	 * 
+	 * @param value
+	 *            the value to wrap in a {@link Value}
+	 * @return the created {@link Value}
+	 */
+	// TODO anehmer on 2017-11-26: rename to toValue()?
+	public static <T> Value<T> V(T value) {
+		return Option.of(value);
+	}
+
+	/**
+	 * Converts the given string values into a {@link Seq} of {@link Value}s
+	 * wrapping the value.
+	 * 
+	 * @param value
+	 *            the {@link Value}s to put in a {@link Seq}
+	 * @return the created {@link Seq} of {@link Value}s
+	 */
+	public static Seq<Value<String>> SL(String... values) {
+		return List.of(values)
+				.map(Option::of);
+	}
+
+	/**
+	 * Converts the given string {@link Value}s into a {@link Seq} of string
+	 * {@link Value}s.
+	 * 
+	 * @param value
+	 *            the {@link Value}s to put in a {@link Seq}
+	 * @return the created {@link Seq} of {@link Value}s
+	 */
+	@SafeVarargs
+	public static Seq<Value<String>> SL(Value<String>... values) {
+		return List.of(values);
+	}
+
+	/**
+	 * Converts the given {@link Value}s into a {@link Seq} of {@link Value}s.
+	 * 
+	 * @param value
+	 *            the {@link Value}s to put in a {@link Seq}
+	 * @return the created {@link Seq} of {@link Value}s
+	 */
+	@SafeVarargs
+	public static Seq<Value<?>> L(Value<?>... values) {
+		return List.of(values);
+	}
+
+	/**
+	 * @see #find(Value, Class, Value, Value, Value...)
+	 */
+	public <T extends Entity> Try<T> find(Value<String> sourceNameSupplier, Class<T> entityClass,
+			Value<String> idSupplier) {
+		return find(sourceNameSupplier, entityClass, idSupplier, (Value<ContextType>) null, null);
+	}
+
+	/**
+	 * @see #find(Value, Class, Value, Value, Value...)
+	 */
+	public <T extends Entity> Try<T> find(Value<String> sourceNameSupplier, Class<T> entityClass,
+			Value<String> idSupplier, Seq<Value<String>> parentIdSuppliers) {
+		return find(sourceNameSupplier, entityClass, idSupplier, (Value<ContextType>) null, parentIdSuppliers);
+	}
+
+	/**
+	 * @see #find(Value, Class, Value, Value, Value...)
+	 */
+	public <T extends Entity> Try<T> find(Value<String> sourceNameSupplier, Class<T> entityClass,
+			Value<String> idSupplier, Value<ContextType> contextTypeSupplier) {
+		return find(sourceNameSupplier, entityClass, idSupplier, contextTypeSupplier, null);
+	}
+
+	/**
+	 * Returns the specified entity by given {@code entityClass} and given
+	 * {@code id}. If the {@code entityClass} is either {@link CatalogAttribute},
+	 * {@link TemplateCompont}, {@link TemplateAttributeAttribute} or
+	 * {@link ContextComponent} the respective root entities
+	 * {@link CatalogComponent}, {@link TemplateRoot} or {@link ContextRoot} are
+	 * used to get the entity to find.
+	 * 
+	 * @param sourceNameSupplier
+	 *            {@link Value} with the name of the source (MDM {@link Environment}
+	 *            name)
+	 * @param entityClass
+	 *            entityType
+	 * @param idSupplier
+	 *            {@link Value} with id of entity to find
+	 * @param contextTypeSupplier
+	 *            {@link Value} with the contextType of entity to find. Can be
+	 *            {@code null} if {@code EntityType} has no {@code ContextType}.
+	 * @param parentIdSuppliers
+	 *            {@link Value}s with the id(s) of parent(s). For
+	 *            {@link CatalogAttribute} the parentId must be the id of the
+	 *            {@link CatalogComponent}, for {@link TemplateComponent} it must be
+	 *            the id of the {@link TemplateRoot}, for {@link TemplateAttribute}
+	 *            it must be the id of the {@link TemplateRoot} first and also the
+	 *            {@link TemplateComponent}, for a nested {@link TemplateComponent}
+	 *            it must be the id of the {@link TemplateRoot} first and the id of
+	 *            the parent {@link TemplateComponent} second, for a
+	 *            {@link TemplateAttribute} within a nested
+	 *            {@link TemplateComponent} it must be the id of the
+	 *            {@link TemplateRoot} first, the id of the parent
+	 *            {@link TemplateComponent} second and the id of the
+	 *            {@link TemplateComponent} last and for {@link ContextComponent} it
+	 *            must be the id of the {@link ContextRoot}.
+	 * @return {@link Try} with the found entity
+	 */
+	// TODO anehmer on 2017-11-22: complete javadoc for ValueListValue and
+	// TemplateTestStepUsage
+	// TODO anehmer on 2017-11-22: add comment for parentIds for TplSensors and
+	// nested TplSensors as well as for TplSensorAttrs and nested TplSensorAttrs
+	@SuppressWarnings("unchecked")
+	public <T extends Entity> Try<T> find(Value<String> sourceNameSupplier, Class<T> entityClass,
+			Value<String> idSupplier, Value<ContextType> contextTypeSupplier, Seq<Value<String>> parentIdSuppliers) {
+
+		// validate parentIds count
+		Map<Class<?>, Integer> minParentsForEntity = HashMap.empty();
+		minParentsForEntity = minParentsForEntity.put(Tuple(CatalogAttribute.class, 1))
+				.put(Tuple(CatalogSensor.class, 1))
+				.put(Tuple(TemplateComponent.class, 1))
+				.put(Tuple(TemplateAttribute.class, 2))
+				.put(Tuple(TemplateSensor.class, 2))
+				.put(Tuple(ValueListValue.class, 1))
+				.put(Tuple(TemplateTestStepUsage.class, 1));
+
+		// return failure if number of parentIds do not correspond with the minimu
+		// required by the entity type
+		Option<Integer> minParents = minParentsForEntity.get(entityClass);
+		// TODO anehmer on 2017-11-25: add entity types to message
+		if (minParents.isDefined() && (parentIdSuppliers == null || minParents.get() > parentIdSuppliers.size())) {
+			return Try.failure(new IllegalArgumentException("ParentId(s) of " + entityClass.getSimpleName()
+					+ " not set appropriately. Expected minimum: " + minParents.get()));
+		}
+
+		// if the find is contextType specific
+		if (contextTypeSupplier != null && !contextTypeSupplier.isEmpty()) {
+			if (entityClass.equals(CatalogAttribute.class)) {
+				// get CatalogAttribute from CatalogComponent
+				if (parentIdSuppliers.size() == 1) {
+					return find(sourceNameSupplier, CatalogComponent.class, parentIdSuppliers.get(0),
+							contextTypeSupplier)
+									.map(catComp -> (T) getChild(CatalogAttribute.class, idSupplier,
+											catComp::getCatalogAttributes));
+				}
+				// get the CatalogAttribute from a CatalogSensor
+				else if (parentIdSuppliers.size() == 2) {
+					return find(sourceNameSupplier, CatalogSensor.class, parentIdSuppliers.get(1), contextTypeSupplier,
+							parentIdSuppliers.dropRight(1))
+									.map(catComp -> (T) getChild(CatalogAttribute.class, idSupplier,
+											catComp::getCatalogAttributes));
+				}
+			}
+
+			// get CatalogSensor from CatalogComponent
+			else if (entityClass.equals(CatalogSensor.class)) {
+				return find(sourceNameSupplier, CatalogComponent.class, parentIdSuppliers.get(0), contextTypeSupplier)
+						.map(catComp -> (T) getChild(CatalogSensor.class, idSupplier, catComp::getCatalogSensors));
+			}
+
+			// get TemplateComponent from TemplateRoot or parent TemplateComponent(s)
+			else if (entityClass.equals(TemplateComponent.class)) {
+				// if nested TplComp has to be found
+				if (parentIdSuppliers.size() > 1) {
+					return find(sourceNameSupplier, TemplateComponent.class,
+							parentIdSuppliers.get(parentIdSuppliers.size() - 1), contextTypeSupplier,
+							parentIdSuppliers.dropRight(1))
+									.map(tplComp -> (T) getChild(TemplateComponent.class, idSupplier,
+											tplComp::getTemplateComponents));
+				}
+				// if non-nested TplComp has to be found: exit condition of recursive call
+				return find(sourceNameSupplier, TemplateRoot.class, parentIdSuppliers.get(0), contextTypeSupplier).map(
+						tplRoot -> (T) getChild(TemplateComponent.class, idSupplier, tplRoot::getTemplateComponents));
+			}
+
+			// get TemplateAttributes from TemplateComponent
+			else if (entityClass.equals(TemplateAttribute.class)) {
+				Try<TemplateComponent> tplCompTry = find(sourceNameSupplier, TemplateComponent.class,
+						parentIdSuppliers.get(parentIdSuppliers.size() - 1), contextTypeSupplier,
+						parentIdSuppliers.dropRight(1));
+				// if TemplateSensorAttribute has to be found
+				if (!tplCompTry.isFailure()) {
+					return tplCompTry.map(tplComp -> (T) getChild(TemplateAttribute.class, idSupplier,
+							tplComp::getTemplateAttributes));
+				} else {
+					return find(sourceNameSupplier, TemplateSensor.class,
+							parentIdSuppliers.get(parentIdSuppliers.size() - 1), contextTypeSupplier,
+							parentIdSuppliers.dropRight(1))
+									.map(tplComp -> (T) getChild(TemplateAttribute.class, idSupplier,
+											tplComp::getTemplateAttributes));
+				}
+			}
+
+			// get TemplateSensor from TemplateComponent
+			else if (entityClass.equals(TemplateSensor.class)) {
+				return find(sourceNameSupplier, TemplateComponent.class,
+						parentIdSuppliers.get(parentIdSuppliers.size() - 1), contextTypeSupplier,
+						parentIdSuppliers.dropRight(1)).map(
+								tplComp -> (T) getChild(TemplateSensor.class, idSupplier, tplComp::getTemplateSensors));
+			}
+
+			// get ContextComponent from ContextRoot
+			else if (entityClass.equals(ContextComponent.class)) {
+				// TODO anehmer on 2017-11-09: implement (also for nested ContextComponents)
+				throw new NotImplementedException("NOT IMPLEMENTED YET");
+			}
+
+			// get root nested entities (CatalogComponent, TemplateRoot, ContextRoot)
+			return getEntityManager(sourceNameSupplier)
+					.mapTry(em -> em.load(entityClass, contextTypeSupplier.get(), idSupplier.get()));
+		}
+
+		// get ValueListValue from ValueList
+		else if (entityClass.equals(ValueListValue.class)) {
+			return find(sourceNameSupplier, ValueList.class, parentIdSuppliers.get(0))
+					.map(valueList -> (T) getChild(ValueListValue.class, idSupplier, valueList::getValueListValues));
+		}
+
+		// get TemplateTestStepUsage from TemplateTest
+		else if (entityClass.equals(TemplateTestStepUsage.class)) {
+			return find(sourceNameSupplier, TemplateTest.class, parentIdSuppliers.get(0))
+					.map(tplTest -> (T) getChild(TemplateTestStepUsage.class, idSupplier,
+							tplTest::getTemplateTestStepUsages));
+		}
+
+		// for all other cases
+		return getEntityManager(sourceNameSupplier).map(em -> em.load(entityClass, idSupplier.get()));
+	}
+
+	/**
+	 * Gets the child with the given {@code childId} or an EntityNotFoundException
+	 * if the child was not found
+	 * 
+	 * @param childClass
+	 *            class of child to construct exception on failure
+	 * @param childIdSupplier
+	 *            supplier of the id of child to find
+	 * @param childSupplier
+	 *            function that gets all children
+	 * @return the found child
+	 */
+	private <T extends Entity> T getChild(Class<T> childClass, Value<String> childIdSupplier,
+			Function0<java.util.List<T>> childSupplier) {
+		return Stream.ofAll(childSupplier.apply())
+				.find(childEntity -> childEntity.getID()
+						.equals(childIdSupplier.get()))
+				.getOrElseThrow(() -> new EntityNotFoundException(childClass, childIdSupplier.get()));
+	}
+
+	/**
+	 * Returns a {@link Try} of all {@link Entity}s if no filter is available.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param entityClass
+	 *            class of the {@link Entity} to find
+	 * @param filter
+	 *            filter string to filter the {@link Entity} result. Can be null.
+	 * @return a {@link Try} of the list of found {@link Entity}s
+	 */
+	// TODO anehmer on 2017-11-26: make filter Value<String>
+	public <T extends Entity> Try<List<T>> findAll(Value<String> sourceNameSupplier, Class<T> entityClass,
+			String filter) {
+		return findAll(sourceNameSupplier, entityClass, filter, null);
+	}
+
+	/**
+	 * Returns a {@link Try} of the matching {@link Entity}s of the given
+	 * contextType using the given filter or all {@link Entity}s of the given
+	 * contextType provided by the {@code contextTypeSupplier} if no filter is
+	 * available.
+	 * 
+	 * @param sourceNameSupplier
+	 *            {@link Value} with the name of the source (MDM {@link Environment}
+	 *            name)
+	 * @param entityClass
+	 *            class of the {@link Entity} to find
+	 * @param filter
+	 *            filter string to filter the {@link Entity} result
+	 * @param contextTypeSupplier
+	 *            a {@link Value} with the contextType of entity to find. Can be
+	 *            {@code null} if {@code EntityType} has no {@code ContextType}.
+	 * @return a {@link Try} of the list of found {@link Entity}s
+	 */
+	// TODO anehmer on 2017-11-26: make filter Value<String>
+	public <T extends Entity> Try<List<T>> findAll(Value<String> sourceNameSupplier, Class<T> entityClass,
+			String filter, Value<ContextType> contextTypeSupplier) {
+		// TODO anehmer on 2017-11-22: do we need to implement the navigationActivity
+		// filter shortcut like in ChannelGroupService.getChannelGroups()
+		if (filter == null || filter.trim()
+				.length() <= 0) {
+			return Try
+					.of(getLoadAllEntitiesMethod(getEntityManager(sourceNameSupplier).get(), entityClass,
+							contextTypeSupplier))
+					.map(javaList -> List.ofAll(javaList));
+		} else {
+			// TODO anehmer on 2017-11-15: not tested
+			return Try
+					.of(() -> this.searchActivity.search(connectorService.getContextByName(sourceNameSupplier.get()),
+							entityClass, filter))
+					.map(javaList -> List.ofAll(javaList));
+		}
+	}
+
+	/**
+	 * Returns the method to load all entities of type {@code entityClass}. If a
+	 * {@code ContextType} is given the appropriate method in
+	 * {@link org.eclipse.mdm.api.dflt.EntityManager} is used
+	 * 
+	 * @param entityManager
+	 *            entityManager to load entities with
+	 * @param entityClass
+	 *            class of entites to load
+	 * @param contextType
+	 *            {@link ContextType} of entities of null if none
+	 * @return the appropriate loadAllEntities() method
+	 */
+	private <T extends Entity> CheckedFunction0<java.util.List<T>> getLoadAllEntitiesMethod(EntityManager entityManager,
+			Class<T> entityClass, Value<ContextType> contextType) {
+		// if contextType is specified
+		if (contextType != null && !contextType.isEmpty()) {
+			return (() -> entityManager.loadAll(entityClass, contextType.get()));
+		}
+		return (() -> entityManager.loadAll(entityClass));
+	}
+
+	/**
+	 * Creates a new {@link Entity} of type entityClass. The method searches the
+	 * {@link EntityFactory} for a suitable create() method by matching the return
+	 * parameter and the given entity class. If more than one method is found, the
+	 * first one is taken. The argument are provided by {@link Try<Object>}s so that
+	 * any exceptions thrown throughout retrieval will be wrapped in the returned
+	 * {@link Try}.
+	 * 
+	 * @param entityClass
+	 *            class of the {@link Entity} to create
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param argumentSuppliers
+	 *            varargs of {@link Try<?>s that supply the create() method
+	 * arguments
+	 * @return a {@link Try} with the created {@link Entity}
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Entity> Try<T> create(Value<String> sourceNameSupplier, Class<T> entityClass,
+			Seq<Value<?>> argumentSuppliers) {
+
+		// get corresponding create method for entityClass from EntityFactory
+		return Try.of(() -> connectorService.getContextByName(sourceNameSupplier.get())
+				.getEntityFactory())
+				.mapTry(factory -> (T) Stream.of(EntityFactory.class.getMethods())
+						// find method with the return type matching entityClass
+						.filter(m -> m.getReturnType()
+								.equals(entityClass))
+						.filter(m -> Arrays.asList(m.getParameterTypes())
+								// compare argument types
+								.equals(argumentSuppliers.map(s -> s.get()
+										.getClass())
+										.toJavaList()))
+						.getOrElseThrow(() -> new NoSuchMethodException(
+								"No matching create()-method found for EntityType " + entityClass.getSimpleName()
+										+ " taking the parameters " + argumentSuppliers.map(s -> s.get()
+												.getClass()
+												.getName())
+												.collect(Collectors.joining(", "))))
+						// invoke with given arguments
+						.invoke(factory.get(), argumentSuppliers.map(Value::get)
+								.toJavaArray()))
+
+				// start transaction to create the entity
+				.map(e -> DataAccessHelper.execute(getEntityManager(sourceNameSupplier).get(), e,
+						DataAccessHelper.CREATE));
+	}
+
+	/**
+	 * Updates the given {@link Entity} with the values of the given map provided by
+	 * the {@code valueMapSupplier}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param entity
+	 *            the entity to update
+	 * @param valueMapSupplier
+	 *            {@link Supplier<Map<String, Object>> of a map of values to update
+	 * the entity with according to matching attribute values by name case sensitive
+	 * @return a {@link Try} of the updated entity
+	 */
+	public <T extends Entity> Try<T> update(Value<String> sourceNameSupplier, Try<T> entity,
+			Value<Map<String, Object>> valueMapSupplier) {
+		// return updated entity
+		return
+		// update entity values
+		entity.map(e -> updateEntityValues(e, valueMapSupplier.get(), sourceNameSupplier))
+				// persist entity
+				.map(e -> DataAccessHelper.execute(getEntityManager(sourceNameSupplier).get(), e.get(),
+						DataAccessHelper.UPDATE));
+	}
+
+	/**
+	 * Deletes the given {@link Entity} {@code valueMapSupplier}.
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param entity
+	 *            the entity to delete
+	 * @return a {@link Try} of the deleted entity
+	 */
+	public <T extends Entity> Try<T> delete(Value<String> sourceNameSupplier, Try<T> entity) {
+		return entity.map(
+				e -> DataAccessHelper.execute(getEntityManager(sourceNameSupplier).get(), e, DataAccessHelper.DELETE));
+	}
+
+	/**
+	 * Returns a {@link Try} of the the {@link SearchAttribute}s for the given
+	 * entityClass
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param entityClass
+	 *            class of the {@link Entity} to get the {@link SearchAttribute}s
+	 *            for
+	 * 
+	 * @return a {@link Try} with the {@link SearchAttribute}s
+	 */
+	public <T extends Entity> Try<List<SearchAttribute>> getSearchAttributesSupplier(Value<String> sourceNameSupplier,
+			Class<T> entityClass) {
+		return Try.of(() -> List.ofAll(this.searchActivity
+				.listAvailableAttributes(connectorService.getContextByName(sourceNameSupplier.get()), entityClass)));
+	}
+
+	/**
+	 * Returns a {@link Try} of the localized {@link Entity} type name
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param entityClass
+	 *            class of the {@link Entity} to be localized
+	 * 
+	 * @return a {@link Try} with the localized {@link Entity} type name
+	 */
+	public <T extends Entity> Try<Map<EntityType, String>> getLocalizeTypeSupplier(Value<String> sourceNameSupplier,
+			Class<T> entityClass) {
+		return Try.of(() -> HashMap.ofAll(this.i18nActivity.localizeType(sourceNameSupplier.get(), entityClass)));
+	}
+
+	/**
+	 * Returns a {@link Try} of the localized {@link Entity} attributes
+	 * 
+	 * @param sourceName
+	 *            name of the source (MDM {@link Environment} name)
+	 * @param entityClass
+	 *            class of the {@link Entity} to be localized
+	 * @return a {@link Try} with the the localized {@link Entity} attributes
+	 */
+	public <T extends Entity> Try<Map<Attribute, String>> getLocalizeAttributesSupplier(
+			Value<String> sourceNameSupplier, Class<T> entityClass) {
+		return Try.of(() -> HashMap.ofAll(this.i18nActivity.localizeAttributes(sourceNameSupplier.get(), entityClass)));
+	}
+
+	/**
+	 * Returns a {@link Try} of an {@link EnumerationValue} for the name supplied by
+	 * the {@code enumValueNameSupplier}
+	 * 
+	 * @param enumValueNameSupplier
+	 *            supplies the name of the {@link EnumerationValue} to get
+	 * @return a {@link Try} with the resolved {@link EnumerationValue}
+	 */
+	public Try<EnumerationValue> getEnumerationValueSupplier(Try<?> enumValueNameSupplier) {
+		return Try.of(() -> EnumRegistry.getInstance()
+				.get(EnumRegistry.VALUE_TYPE)
+				.valueOf(enumValueNameSupplier.get()
+						.toString()));
+	}
+
+	/**
+	 * Gets the EntityManager from the ConnectorService with the given source name
+	 * provided by the {@code sourceNameSupplier}.
+	 * 
+	 * @param sourceNameSupplier
+	 *            {@link Value} with the name of the datasource to get EntityManager
+	 *            for
+	 * @return the found EntityManager. Throws {@link MDMEntityAccessException} if
+	 *         not found.
+	 */
+	private Try<EntityManager> getEntityManager(Value<String> sourceNameSupplier) {
+		return Try.of(() -> this.connectorService.getContextByName(sourceNameSupplier.get())
+				.getEntityManager()
+				.orElseThrow(() -> new MDMEntityAccessException("Entity manager not present")));
+	}
+
+	/**
+	 * Updates the given {@link Entity} with the values from the given valueMap. All
+	 * matching attributes (case sensitive) are updated as well as the referenced
+	 * relations by the id of the given
+	 * {@link org.eclipse.mdm.api.base.model.Entity} and the simple class name as
+	 * the key (the data model attribute name is the reference, case sensitive).
+	 * 
+	 * @param entity
+	 *            the entity to update
+	 * @param valueMap
+	 *            values to update the entity with according to matching attribute
+	 *            names. The keys are compared case sensitive.
+	 * @return a {@link Try} with the the updated entity
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Entity> Try<T> updateEntityValues(T entity, Map<String, Object> valueMap,
+			Value<String> sourceNameSupplier) {
+
+		HashMap<String, org.eclipse.mdm.api.base.model.Value> entityValues = HashMap.ofAll(entity.getValues());
+
+		// update primitive values where the key from the valueMap has a matching entity
+		// value and collect the updated keys
+		Set<String> updatedPrimitiveValues = valueMap
+				.filter((valueMapEntryKey, valueMapEntryValue) -> entityValues.containsKey(valueMapEntryKey)
+						&& !(valueMapEntryValue instanceof java.util.Map))
+				.map((entityValueEntryKey, entityValueEntryValue) -> {
+					entityValues.get(entityValueEntryKey)
+							.forEach(value -> value.set(entityValueEntryValue));
+					return new Tuple2<>(entityValueEntryKey, entityValueEntryValue);
+				})
+				.keySet();
+
+		// update enumeration values
+		Set<String> updatedEnumerationValues = valueMap
+				.filter((valueMapEntryKey, valueMapEntryValue) -> entityValues.containsKey(valueMapEntryKey)
+						&& (valueMapEntryValue instanceof java.util.Map))
+				.map((entityValueEntryKey, entityValueEntryValue) -> {
+					entityValues.get(entityValueEntryKey)
+							.forEach(value -> {
+								// get key-value-pairs that identify the enum und enumValue
+								String enumName = ((java.util.Map<String, String>) entityValueEntryValue)
+										.get("Enumeration");
+								String enumValueName = ((java.util.Map<String, String>) entityValueEntryValue)
+										.get("EnumerationValue");
+
+								if (enumName == null || enumValueName == null) {
+									throw new IllegalArgumentException("EnumerationValue is set by providing a map "
+											+ "containing the keys 'Enumeration' and 'EnumerationValue' "
+											+ "and the respective names as the values");
+								}
+
+								// find enumeration and the enumeration value
+								Option.of(EnumRegistry.getInstance()
+										// get enum
+										.get(enumName))
+										.onEmpty(() -> {
+											throw new IllegalArgumentException(
+													"Enumeration [" + enumName + "] not found");
+										})
+										// get enumValue
+										.map(enumeration -> enumeration.valueOf(enumValueName))
+										// if enumValue is not found, null is returned
+										.filter(Objects::nonNull)
+										.onEmpty(() -> {
+											throw new IllegalArgumentException("EnumerationValue [" + enumValueName
+													+ "] not found in Enumeration [" + enumName + "]");
+										})
+										// set enumValue
+										.map(enumValue -> {
+											value.set(enumValue);
+											return enumValue;
+										});
+
+							});
+					return new Tuple2<>(entityValueEntryKey, entityValueEntryValue);
+				})
+				.keySet();
+
+		// update the relations and gather the updated keys
+		// use only those keys that have not been updated yet and can be resolved as
+		// class names. If so, try to update accordingly named relation with the entity
+		// found by its id given as the value
+		Set<String> updatedRelations = valueMap
+				.filter((valueMapEntryKey, valueMapEntryValue) -> !updatedPrimitiveValues.contains(valueMapEntryKey)
+						&& !updatedEnumerationValues.contains(valueMapEntryKey))
+				.filter((relatedEntityClassName, relatedEntityId) -> {
+					EntityStore store = getMutableStore(entity);
+
+					ContextType contextType = null;
+					// determine if class has a context type
+					for (ContextType ct : ContextType.values()) {
+						int index = relatedEntityClassName.toUpperCase()
+								.indexOf(ct.name());
+						if (index > 0) {
+							contextType = ct;
+							// cut out ContextType
+							relatedEntityClassName = relatedEntityClassName.substring(0, index)
+									+ relatedEntityClassName.substring(index + ct.name()
+											.length());
+						}
+					}
+
+					// to have final variables for Try
+					final String processedRelatedEntityClassName = relatedEntityClassName;
+					final ContextType contextTypeIfPresent = contextType;
+
+					// load class from model packages
+					Try<Class<Entity>> updateTry = Try
+							.of(() -> (Class<Entity>) Class
+									.forName("org.eclipse.mdm.api.base.model." + processedRelatedEntityClassName))
+							.orElse(Try.of(() -> (Class<Entity>) Class
+									.forName("org.eclipse.mdm.api.dflt.model." + processedRelatedEntityClassName)))
+							// update related entity by first finding the related entity by its id
+							// use find and store.set() with ContextType if needed
+							.andThenTry(entityClass -> {
+								if (contextTypeIfPresent == null) {
+									store.set(find(sourceNameSupplier, entityClass, V(relatedEntityId.toString()))
+											.onFailure(e -> LOGGER.error(e.getMessage()))
+											.get());
+								} else {
+									store.set(find(sourceNameSupplier, entityClass, V(relatedEntityId.toString()),
+											V(contextTypeIfPresent)).onFailure(e -> LOGGER.error(e.getMessage()))
+													.get(),
+											contextTypeIfPresent);
+								}
+							})
+							.onFailure(e -> LOGGER.error("Entity of type [" + processedRelatedEntityClassName
+									+ "] and ID " + relatedEntityId + " not found", e));
+
+					return updateTry.isSuccess() ? true : false;
+				})
+				.keySet();
+
+		// return Try.Failure if there are keys that are not present in the entity and
+		// thus are not updated
+		String unmappedKeys = valueMap
+				.filterKeys(key -> !updatedPrimitiveValues.contains(key) && !updatedEnumerationValues.contains(key)
+						&& !updatedRelations.contains(key))
+				.map(Tuple::toString)
+				.collect(Collectors.joining(", "));
+
+		if (unmappedKeys != null && !unmappedKeys.isEmpty()) {
+			return Try.failure(
+					new IllegalArgumentException("ValueMap to update entity contains the following keys that either "
+							+ "have no match in the entity values or relations to update "
+							+ "or an error occurred while finding the related entity: " + unmappedKeys));
+		} else {
+			return Try.of(() -> entity);
+		}
+	}
+
+	/**
+	 * Get the mutableStore from {@link org.eclipse.mdm.api.base.adapter.Core} of
+	 * given {@link org.eclipse.mdm.api.base.model.Entity}
+	 * 
+	 * @param e
+	 *            Entity to get Core of
+	 * @return Core of given Entity
+	 */
+	private static EntityStore getMutableStore(Entity e) {
+		return Try.of(() -> {
+			Method getMetod;
+			try {
+				getMetod = BaseEntity.class.getDeclaredMethod("getCore");
+				getMetod.setAccessible(true);
+			} catch (NoSuchMethodException | SecurityException x) {
+				throw new IllegalStateException(
+						"Unable to load 'getCore()' in class '" + BaseEntity.class.getSimpleName() + "'.", x);
+			}
+			Core core = (Core) getMetod.invoke(e);
+			return core.getMutableStore();
+		})
+				.get();
+	}
+
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/EntityNotFoundException.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/EntityNotFoundException.java
new file mode 100755
index 0000000..7e8f7cc
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/EntityNotFoundException.java
@@ -0,0 +1,54 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.utils;
+
+import org.eclipse.mdm.api.base.model.Entity;
+
+/**
+ * Exception thrown if entity could not be retrieved from the datastore
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+public class EntityNotFoundException extends RuntimeException {
+
+	private static final long serialVersionUID = 6862157710262117670L;
+
+	/**
+	 * Default constructor with causing exception
+	 * 
+	 * @param entityClass
+	 *            Class of entity that could not be found
+	 * @param id
+	 *            id of entity that could not be found
+	 * @param x
+	 *            the cause of the exception
+	 */
+	public EntityNotFoundException(Class<? extends Entity> entityClass, String id, Throwable x) {
+		super(entityClass.getSimpleName() + " with ID " + id + " not found.", x);
+	}
+
+	/**
+	 * Default constructor without causing exception
+	 * 
+	 * @param entityClass
+	 *            Class of entity that could not be found
+	 * @param id
+	 *            id of entity that could not be found
+	 */
+	public EntityNotFoundException(Class<? extends Entity> entityClass, String id) {
+		super(entityClass.getSimpleName() + " with ID " + id + " not found.");
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ISODateDeseralizer.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ISODateDeseralizer.java
new file mode 100755
index 0000000..6189896
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ISODateDeseralizer.java
@@ -0,0 +1,62 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.utils;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonTokenId;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;
+
+/**
+ * JSON Deserializer for ISO 8601 compliant dates with format
+ * 
+ * <pre>
+ * yyyy-MM-dd'T'HH:mm:ss'Z'
+ * </pre>
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+class ISODateDeseralizer extends UntypedObjectDeserializer {
+
+	private static final long serialVersionUID = 1L;
+
+	transient DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
+
+	/**
+	 * Deserialize JSON and try to parse every String as an ISO8601 date
+	 */
+	@Override
+	public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+		// try to parse every string as a date
+		// TODO anehmer on 2018-04-30: this approach could lead to a performance leak as
+		// every incoming string is tried to be converted into a date though the
+		// appraoch is very generic
+		if (jp.getCurrentTokenId() == JsonTokenId.ID_STRING) {
+			try {
+				return LocalDateTime.parse(jp.getText(), dateFormatter);
+			} catch (Exception e) {
+				return super.deserialize(jp, ctxt);
+			}
+		} else {
+			return super.deserialize(jp, ctxt);
+		}
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/RequestBody.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/RequestBody.java
new file mode 100644
index 0000000..e3d469b
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/RequestBody.java
@@ -0,0 +1,139 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.utils;
+
+import java.util.NoSuchElementException;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import io.vavr.API;
+import io.vavr.CheckedFunction0;
+import io.vavr.Lazy;
+import io.vavr.collection.HashMap;
+import io.vavr.collection.Map;
+import io.vavr.control.Try;
+
+/**
+ * Class representing the JSON request body as a accessible map of values. The
+ * parsing occurs lazily on first get() on a value and as the {@link Lazy}
+ * result is memoized, the body is only parsed once. Any exceptions due to
+ * parsing or non-existing keys are thrown on get() call.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+public final class RequestBody {
+
+	private Lazy<HashMap<String, Object>> requestBodyMap;
+	private static ObjectMapper mapper;
+
+	static {
+		mapper = new ObjectMapper();
+		SimpleModule simpleModule = new SimpleModule();
+		simpleModule.addDeserializer(Object.class, new ISODateDeseralizer());
+		mapper.registerModule(simpleModule);
+	}
+
+	/**
+	 * Just hide the default constructor
+	 */
+	private RequestBody() {
+	}
+
+	/**
+	 * Builds RequestBody by using the given function that returns the request body
+	 * as a parsed {@link Map<String, Object>.
+	 * 
+	 * @param requestBodyMapSupplier
+	 *            function that return a {@link Map<String, Object> with the parsed
+	 *            JSON body
+	 */
+	private RequestBody(CheckedFunction0<HashMap<String, Object>> requestBodyMapSupplier) {
+		// make the function lazily evaluated, so that any parsing exception occur on
+		// first get() on a value
+		// unchecked() makes it possible to trick the compiler to not force us to deal
+		// with the checked exceptions here
+		this.requestBodyMap = Lazy.of(API.unchecked(requestBodyMapSupplier));
+	}
+
+	/**
+	 * Creates a RequestBody
+	 * 
+	 * @param body
+	 *            JSON request body to create RequestBody
+	 * @return the RequestBody
+	 */
+	public static RequestBody create(String requestBodyString) {
+		// create requestbody with a function that parses the json request and
+		// transforms it to a {@link Map<String, Object>
+		return new RequestBody(() -> HashMap
+				.ofAll(mapper.readValue(requestBodyString, new TypeReference<java.util.Map<String, Object>>() {
+				})));
+	}
+
+	/**
+	 * Returns a {@link Try> that holds the string value for the given key. If the
+	 * underlying request body map can be parsed, the appropriate JSON exceptions
+	 * are thrown if get() is called on the {@link Try>. If the key was not found, a
+	 * {@link NoSuchElementException} is thrown correspondingly.
+	 * 
+	 * @param key
+	 *            key to get value for
+	 * @return the string value for the given key
+	 */
+	public Try<String> getStringValueSupplier(String key) {
+		return Try.of(() -> Lazy.of(() -> requestBodyMap.get()
+				.get(key)
+				.map(Object::toString)
+				.onEmpty(() -> {
+					throw new NoSuchElementException("Key [" + key + "] not found in request body.");
+				})
+				.get())
+				.get());
+	}
+
+	/**
+	 * Returns a {@link Try<Object>> that holds the value for the given key. If the
+	 * underlying request body map can be parsed, the appropriate JSON exceptions
+	 * are thrown if get() is called on the {@link Try>. If the key was not found, a
+	 * {@link NoSuchElementException} is thrown correspondingly.
+	 * 
+	 * @param key
+	 *            key to get value for
+	 * @return the value for the given key
+	 */
+	public Try<Object> getValueSupplier(String key) {
+		return Try.of(() -> Lazy.of(() -> requestBodyMap.get()
+				.get(key)
+				.map(Object::toString)
+				.onEmpty(() -> {
+					throw new NoSuchElementException("Key [" + key + "] not found in request body.");
+				})
+				.get())
+				.get());
+	}
+
+	/**
+	 * Returns a {@link Try} of the complete {@link Map<String, Object>} of the
+	 * request body
+	 * 
+	 * @return a {@link Try} of {@link Map<String, Object>} of the request body
+	 */
+	public Try<Map<String, Object>> getValueMapSupplier() {
+		return Try.of(() -> requestBodyMap.get());
+	}
+}
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
new file mode 100644
index 0000000..7f6f4c2
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/ServiceUtils.java
@@ -0,0 +1,304 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.utils;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+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.api.base.ServiceNotProvidedException;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.adapter.ModelManager;
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.ComparisonOperator;
+import org.eclipse.mdm.api.base.query.Condition;
+import org.eclipse.mdm.api.base.query.Filter;
+import org.eclipse.mdm.api.base.query.FilterItem;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.api.dflt.model.ValueList;
+import org.eclipse.mdm.businessobjects.control.FilterParser;
+import org.eclipse.mdm.businessobjects.entity.I18NResponse;
+import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
+import org.eclipse.mdm.businessobjects.entity.SearchAttributeResponse;
+import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.slf4j.LoggerFactory;
+
+import io.vavr.Value;
+import io.vavr.collection.Stream;
+import io.vavr.control.Try;
+
+public final class ServiceUtils {
+
+	private ServiceUtils() {
+	}
+
+	/**
+	 * returns true if the given filter String is a parent filter of the given
+	 * parent type
+	 *
+	 * @param em
+	 *            {@link EntityManager} of the data source
+	 * @param filter
+	 *            parent filter string to check
+	 * @param parentType
+	 *            class of the parent entity
+	 * @return true if the give filter String is a parent filter
+	 */
+	public static boolean isParentFilter(ApplicationContext context, String filter,
+			Class<? extends Entity> parentType) {
+		ModelManager mm = context.getModelManager()
+				.orElseThrow(() -> new ServiceNotProvidedException(ModelManager.class));
+		EntityType et = mm.getEntityType(parentType);
+
+		Filter f = FilterParser.parseFilterString(mm.listEntityTypes(), filter);
+
+		List<FilterItem> filterItems = f.stream().collect(Collectors.toList());
+		
+		if (filterItems.size() == 1 && filterItems.get(0).isCondition()) {
+			Condition c = filterItems.get(0).getCondition();
+			return et.getIDAttribute().equals(c.getAttribute()) && ComparisonOperator.EQUAL.equals(c.getComparisonOperator());
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * returns the business object ID from a parent filter
+	 *
+	 * @param em
+	 *            {@link EntityManager} of the data source
+	 * @param filter
+	 *            parent filter string
+	 * @param parentType
+	 *            parent type to identify the Id attribute name
+	 * @return the extracted business object Id
+	 * @throws IllegalArgumentException if the given filter is not a parent filter, 
+	 * this means the filter does not have exactly one condition on the parent's 
+	 * ID attribute with {@link ComparisonOperator#EQUAL}
+	 */
+	public static String extactIdFromParentFilter(ApplicationContext context, String filter,
+			Class<? extends Entity> parentType) {
+		ModelManager mm = context.getModelManager()
+				.orElseThrow(() -> new ServiceNotProvidedException(ModelManager.class));
+		EntityType et = mm.getEntityType(parentType);
+
+		Filter f = FilterParser.parseFilterString(mm.listEntityTypes(), filter);
+
+		List<FilterItem> filterItems = f.stream().collect(Collectors.toList());
+		
+		if (filterItems.size() == 1 && filterItems.get(0).isCondition()) {
+			Condition c = filterItems.get(0).getCondition();
+			if (et.getIDAttribute().equals(c.getAttribute()) && ComparisonOperator.EQUAL.equals(c.getComparisonOperator()))
+			{
+				return c.getValue().extract(ValueType.STRING);
+			}
+		}
+		
+		throw new IllegalArgumentException("Cannot extract parent ID. Filter is not a parent filter: " + filter);
+	}
+
+	/**
+	 * 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();
+		}
+	}
+
+	/**
+	 * Builds {@Link Response} from given {@link Entity}
+	 * 
+	 * @param entity
+	 *            {@link Entity} to build {@link Response} from
+	 * @return the build {@link Response}
+	 */
+	public static <T extends Entity> Response buildEntityResponse(T entity, Status status) {
+		if (entity != null) {
+			MDMEntityResponse response = new MDMEntityResponse(entity.getClass(), entity);
+			// TODO anehmer on 2018-02-08: relations should be included in the output
+			GenericEntity<Object> genEntity = new GenericEntity<Object>(response, response.getClass());
+			return Response.status(status)
+					.entity(genEntity)
+					.type(MediaType.APPLICATION_JSON)
+					.build();
+		} else {
+			return Response.status(Status.NO_CONTENT)
+					.type(MediaType.APPLICATION_JSON)
+					.build();
+		}
+	}
+
+	/**
+	 * Builds {@Link Response} from given {@link Entity}
+	 * 
+	 * @param entity
+	 *            {@link Entity} to build {@link Response} from
+	 * @return the build {@link Response}
+	 */
+	public static <T extends Entity> Response buildEntityResponse(io.vavr.collection.List<T> entities, Status status) {
+		if (entities.nonEmpty()) {
+			@SuppressWarnings("unchecked")
+			Class<T> entityClass = (Class<T>) entities.get()
+					.getClass();
+			MDMEntityResponse response = new MDMEntityResponse(entityClass, entities.asJava());
+			GenericEntity<Object> genEntity = new GenericEntity<Object>(response, response.getClass());
+			return Response.status(status)
+					.entity(genEntity)
+					.type(MediaType.APPLICATION_JSON)
+					.build();
+		} else {
+			return Response.status(Status.NO_CONTENT)
+					.type(MediaType.APPLICATION_JSON)
+					.build();
+		}
+	}
+
+	/**
+	 * Builds {@Link Response} from given {@link Entity}
+	 * 
+	 * @param entity
+	 *            {@link Entity} to build {@link Response} from
+	 * @return the build {@link Response}
+	 */
+	public static <T extends Entity> Response buildErrorResponse(Throwable t, Status status) {
+		return Response.status(status)
+				.entity(t)
+				.type(MediaType.APPLICATION_JSON)
+				.build();
+	}
+
+	/**
+	 * Converts the given object to a {@link Response} with the given {@link Status}
+	 *
+	 * @param response
+	 *            object to convert
+	 * @param status
+	 *            {@link Status} of the {@link Response}
+	 * @return the created {@link Response}
+	 */
+	public static Response toResponse(Object response, Status status) {
+		GenericEntity<Object> genEntity = new GenericEntity<Object>(response, response.getClass());
+		return Response.status(status)
+				.entity(genEntity)
+				.type(MediaType.APPLICATION_JSON)
+				.build();
+	}
+
+	/**
+	 * Return the search attributes for the {@link ValueList} type.
+	 * 
+	 * @param sourceNameSupplier
+	 *            {@link Value} with the name of the source (MDM {@link Environment}
+	 *            name)
+	 * @param entityClass
+	 *            {@link Entity} class to get localization data for
+	 * @param entityService
+	 *            {@link EntityService} used to get localization data
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	public static <T extends Entity> Response buildSearchAttributesResponse(Value<String> sourceNameSupplier,
+			Class<T> entityClass, EntityService entityService) {
+		return entityService.getSearchAttributesSupplier(sourceNameSupplier, entityClass)
+				.map(searchAttributes -> ServiceUtils
+						.toResponse(new SearchAttributeResponse(searchAttributes.toJavaList()), Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * Return the localized type and attributes for the {@link Entity} type.
+	 * 
+	 * @param sourceNameSupplier
+	 *            {@link Value} with the name of the source (MDM {@link Environment}
+	 *            name)
+	 * @param entityClass
+	 *            {@link Entity} class to get localization data for
+	 * @param entityService
+	 *            {@link EntityService} used to get localization data
+	 * @return the {@link Response} with the localized data
+	 */
+	public static <T extends Entity> Response buildLocalizationResponse(Value<String> sourceNameSupplier,
+			Class<T> entityClass, EntityService entityService) {
+		return Try
+				.of(() -> ServiceUtils.toResponse(new I18NResponse(
+						entityService.getLocalizeTypeSupplier(sourceNameSupplier, entityClass)
+								.get()
+								.toJavaMap(),
+						entityService.getLocalizeAttributesSupplier(sourceNameSupplier, entityClass)
+								.get()
+								.toJavaMap()),
+						Status.OK))
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER)
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
+	 * A Response representing a server error.
+	 */
+	public final static Response SERVER_ERROR_RESPONSE = Response.serverError()
+			.build();
+
+	/**
+	 * Builds an error response based on an exception to be sent to the client
+	 */
+	public static final Function<? super Throwable, Response> ERROR_RESPONSE_SUPPLIER = (e) -> {
+		LoggerFactory.getLogger(ServiceUtils.class)
+				.error(e.getMessage(), e);
+		// TODO anehmer on 2017-11-22: customize status according to exception
+		return Response.status(Status.INTERNAL_SERVER_ERROR)
+				.entity(e.getStackTrace()[0].getClassName() + "." + e.getStackTrace()[0].getMethodName() + ": "
+						+ e.getMessage())
+				.type(MediaType.APPLICATION_JSON)
+				.build();
+	};
+
+	/**
+	 * Returns a {@link Try} to get the {@link ContextType} for the provided name
+	 * 
+	 * @param contextTypeName
+	 *            name of the {@link ContextType}
+	 * @return a {@link Try} of the {@link ContextType} for the given name
+	 */
+	public static Try<ContextType> getContextTypeSupplier(String contextTypeName) {
+		return Stream.of(ContextType.values())
+				.filter(contextType -> contextType.name()
+						.equals(contextTypeName.toUpperCase()))
+				.toTry();
+	}
+}
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..543d7d6
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/boundary/QueryResource.java
@@ -0,0 +1,66 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..f310ee7
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/boundary/QueryService.java
@@ -0,0 +1,179 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.ServiceNotProvidedException;
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.adapter.ModelManager;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.base.query.Filter;
+import org.eclipse.mdm.api.base.query.Result;
+import org.eclipse.mdm.api.base.search.SearchService;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.businessobjects.control.FilterParser;
+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";
+
+	@Inject
+	ConnectorService connectorService;
+
+	public List<Row> queryRows(QueryRequest request) {
+		List<Row> rows = new ArrayList<>();
+
+		for (SourceFilter filter : request.getFilters()) {
+			try {
+				ApplicationContext context = this.connectorService.getContextByName(filter.getSourceName());
+
+				rows.addAll(queryRowsForSource(context, 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()) {
+
+			ApplicationContext context = this.connectorService.getContextByName(envName);
+			Optional<ModelManager> mm = context.getModelManager();
+			Optional<org.eclipse.mdm.api.base.query.QueryService> qs = context.getQueryService();
+
+			if (mm.isPresent() && qs.isPresent()) {
+
+				try {
+					EntityType entityType = mm.get().getEntityType(suggestionRequest.getType());
+
+					Attribute attr = entityType.getAttribute(suggestionRequest.getAttrName());
+
+					suggestions.addAll(qs.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(ApplicationContext context, String resultEntity, List<String> columns, String filterString,
+			String searchString) throws DataAccessException {
+
+
+		ModelManager modelManager = context.getModelManager()
+				.orElseThrow(() -> new ServiceNotProvidedException(ModelManager.class));
+
+		SearchService searchService = context.getSearchService()
+				.orElseThrow(() -> new IllegalStateException("neccessary SearchService is not available"));
+
+		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 = FilterParser.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 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..f40c588
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/Column.java
@@ -0,0 +1,99 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..5e9173e
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/EntityId.java
@@ -0,0 +1,51 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.query.entity;

+

+/**

+ * 

+ * @author Matthias Koller, Peak Solution GmbH

+ *

+ */

+public class EntityId {

+	private String source;

+	private String type;

+	private String 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 String getId() {

+		return id;

+	}

+

+	public void setId(String 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..8eb7b6a
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/LoadRequest.java
@@ -0,0 +1,43 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..5d4c4c9
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/QueryRequest.java
@@ -0,0 +1,53 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..6b4a5c7
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/QueryResult.java
@@ -0,0 +1,38 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..5370e92
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/Row.java
@@ -0,0 +1,101 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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 String 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 String getId() {

+		return id;

+	}

+

+	public void setId(String 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..9d1b883
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SourceFilter.java
@@ -0,0 +1,50 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..3d39994
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SuggestionRequest.java
@@ -0,0 +1,69 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..938adab
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/entity/SuggestionResponse.java
@@ -0,0 +1,54 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..40196fd
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/query/util/Util.java
@@ -0,0 +1,71 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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.adapter.EntityType;

+import org.eclipse.mdm.api.base.model.Entity;

+import org.eclipse.mdm.api.base.model.Value;

+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 final 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/main/java/org/eclipse/mdm/shoppingbasket/boundary/ShoppingBasketResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/boundary/ShoppingBasketResource.java
new file mode 100644
index 0000000..084a89b
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/boundary/ShoppingBasketResource.java
@@ -0,0 +1,209 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.shoppingbasket.boundary;

+

+import java.net.URI;

+import java.util.ArrayList;

+import java.util.List;

+import java.util.Map;

+import java.util.stream.Collectors;

+

+import javax.inject.Inject;

+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.Context;

+import javax.ws.rs.core.MediaType;

+import javax.ws.rs.core.Response;

+import javax.ws.rs.core.Response.Status;

+import javax.ws.rs.core.UriInfo;

+

+import org.eclipse.mdm.api.base.model.Channel;

+import org.eclipse.mdm.api.base.model.ChannelGroup;

+import org.eclipse.mdm.api.base.model.Entity;

+import org.eclipse.mdm.api.base.model.Measurement;

+import org.eclipse.mdm.api.base.model.Test;

+import org.eclipse.mdm.api.base.model.TestStep;

+import org.eclipse.mdm.api.dflt.ApplicationContext;

+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;

+import org.eclipse.mdm.shoppingbasket.entity.BasketItem;

+import org.eclipse.mdm.shoppingbasket.entity.MDMItem;

+import org.eclipse.mdm.shoppingbasket.entity.ShoppingBasket;

+import org.eclipse.mdm.shoppingbasket.entity.ShoppingBasketRequest;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+import com.google.common.collect.ImmutableMap;

+

+/**

+ * {@link ShoppingBasketResource} resource

+ *

+ */

+@Path("/shoppingbasket")

+public class ShoppingBasketResource {

+

+	private static final Logger LOG = LoggerFactory.getLogger(ShoppingBasketResource.class);

+

+	private ConnectorService connectorService;

+

+	@Context

+	private UriInfo uriInfo;

+

+	private static final Map<Class<? extends Entity>, String> ENTITY2FRAGMENT_URI = ImmutableMap

+			.<Class<? extends Entity>, String>builder()

+			.put(Project.class, "projects")

+			.put(Pool.class, "pools")

+			.put(Test.class, "tests")

+			.put(TestStep.class, "teststeps")

+			.put(Measurement.class, "measurements")

+			.put(ChannelGroup.class, "channelgroups")

+			.put(Channel.class, "channels")

+			.build();

+

+	private static final Map<String, Class<? extends Entity>> ENTITYNAME2CLASS = ImmutableMap

+			.<String, Class<? extends Entity>>builder()

+			.put("Project", Project.class)

+			.put("Pool", Pool.class)

+			.put("Test", Test.class)

+			.put("TestStep", TestStep.class)

+			.put("Measurement", Measurement.class)

+			.put("ChannelGroup", ChannelGroup.class)

+			.put("Channel", Channel.class)

+			.build();

+

+	/**

+	 * @param connectorService

+	 *            {@link ConnectorService}

+	 */

+	@Inject

+	ShoppingBasketResource(ConnectorService connectorService) {

+		this.connectorService = connectorService;

+	}

+

+	/**

+	 * Returns a shopping basket XML file containing the requested MDM items

+	 * 

+	 * @param items

+	 *            list with items the shopping basket should contain.

+	 * @return a XML file the the shopping basket information.

+	 */

+	@POST

+	@Consumes(MediaType.APPLICATION_JSON)

+	@Produces(MediaType.APPLICATION_XML)

+	public Response getShoppingbasket(ShoppingBasketRequest items) {

+		try {

+			return Response.ok(convertItemsToShoppingBasket(items), MediaType.APPLICATION_XML_TYPE).build();

+		} catch (RuntimeException e) {

+			LOG.error(e.getMessage(), e);

+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);

+		}

+

+	}

+

+	/**

+	 * Converts a {@link ShoppingBasketRequest} into a {@link ShoppingBasket} by

+	 * generating the link and REST URIs for the given MDM items.

+	 * 

+	 * @param items

+	 *            MDM items to convert

+	 * @return a shopping basket representing the given MDM items

+	 */

+	private ShoppingBasket convertItemsToShoppingBasket(ShoppingBasketRequest items) {

+		List<BasketItem> basketItems = new ArrayList<>();

+		Map<String, List<MDMItem>> itemsBySource = items.getItems().stream()

+				.collect(Collectors.groupingBy(MDMItem::getSource));

+

+		for (Map.Entry<String, List<MDMItem>> entry : itemsBySource.entrySet()) {

+			ApplicationContext context = connectorService.getContextByName(entry.getKey());

+			EntityManager em = context.getEntityManager()

+					.orElseThrow(() -> new IllegalArgumentException(

+							"Not connected to ApplicationContext with name " + entry.getKey()));

+

+			List<Entity> entities = entry.getValue().stream().map(i -> getEntity(em, i)).collect(Collectors.toList());

+

+			for (Map.Entry<Entity, String> e : em.getLinks(entities).entrySet()) {

+

+				BasketItem basketItem = new BasketItem();

+				basketItem.setSource(context.getAdapterType());

+				basketItem.setLink(e.getValue());

+				basketItem.setRestURI(getURIForEntity(e.getKey()));

+

+				basketItems.add(basketItem);

+			}

+		}

+

+		ShoppingBasket basket = new ShoppingBasket();

+		basket.setName(items.getName());

+		basket.setItems(basketItems);

+		return basket;

+	}

+

+	/**

+	 * Generate the REST URI for a Entity

+	 * 

+	 * @param entity

+	 *            Entity

+	 * @return the REST URI of the given Entity

+	 */

+	private URI getURIForEntity(Entity entity) {

+		return uriInfo.getBaseUriBuilder()

+				.path("environments")

+				.path(entity.getSourceName())

+				.path(getURIFragmentForEntity(entity))

+				.path(entity.getID())

+				.build();

+	}

+

+	/**

+	 * Returns the URI fragment of a entity. For example, if entity is an instance

+	 * of {@link TestStep} the string "teststeps" is returned.

+	 * 

+	 * @param entity

+	 * @return the URI fragment of the Entity's EntityType

+	 */

+	private String getURIFragmentForEntity(Entity entity) {

+

+		for (Map.Entry<Class<? extends Entity>, String> entry : ENTITY2FRAGMENT_URI.entrySet()) {

+			if (entry.getKey().isInstance(entity)) {

+				return entry.getValue();

+			}

+		}

+		// fallback to generic fragment

+		return entity.getClass().getSimpleName().toLowerCase() + "s";

+	}

+

+	/**

+	 * Loads the entity specified by the given MDM item.

+	 * 

+	 * @param em

+	 *            {@link EntityManager} used to load the entity.

+	 * @param item

+	 *            MDM item to load based on its type and id.

+	 * @return the loaded Entity

+	 */

+	private Entity getEntity(EntityManager em, MDMItem item) {

+		for (Map.Entry<String, Class<? extends Entity>> entry : ENTITYNAME2CLASS.entrySet()) {

+			if (item.getType().equals(entry.getKey())) {

+				return em.load(entry.getValue(), item.getId());

+			}

+		}

+		throw new IllegalArgumentException("Cannot load type: " + item.getType());

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/BasketItem.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/BasketItem.java
new file mode 100644
index 0000000..70df56f
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/BasketItem.java
@@ -0,0 +1,82 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.shoppingbasket.entity;

+

+import java.net.URI;

+

+import javax.xml.bind.annotation.XmlElement;

+import javax.xml.bind.annotation.XmlRootElement;

+import javax.xml.bind.annotation.XmlType;

+

+/**

+ * BasketItem represents a single item in a shopping basket.

+ *

+ */

+@XmlRootElement(name = "item")

+@XmlType(propOrder = { "source", "restURI", "link" })

+public class BasketItem {

+

+	private String source;

+	private URI restURI;

+	private String link;

+

+	/**

+	 * @return the adapter source of this item

+	 */

+	@XmlElement(name = "source")

+	public String getSource() {

+		return source;

+	}

+

+	/**

+	 * @param type

+	 *            the adapter source of this item

+	 */

+	public void setSource(String source) {

+		this.source = source;

+	}

+

+	/**

+	 * @return rest URI of this item

+	 */

+	@XmlElement(name = "resturi")

+	public URI getRestURI() {

+		return restURI;

+	}

+

+	/**

+	 * @param restURI

+	 *            rest URI of this item

+	 */

+	public void setRestURI(URI restURI) {

+		this.restURI = restURI;

+	}

+

+	/**

+	 * @return link to this item in the adapter's data store

+	 */

+	@XmlElement(name = "link")

+	public String getLink() {

+		return link;

+	}

+

+	/**

+	 * @param link

+	 *            to this item in the adapter's data store

+	 */

+	public void setLink(String link) {

+		this.link = link;

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/MDMItem.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/MDMItem.java
new file mode 100644
index 0000000..e0930f3
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/MDMItem.java
@@ -0,0 +1,70 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.shoppingbasket.entity;

+

+/**

+ * MDMItem contains all information (source, type, id) to identify a MDM Entity.

+ *

+ */

+public class MDMItem {

+	private String source;

+	private String type;

+	private String id;

+

+	/**

+	 * @return the source

+	 */

+	public String getSource() {

+		return source;

+	}

+

+	/**

+	 * @param source

+	 *            the source to set

+	 */

+	public void setSource(String source) {

+		this.source = source;

+	}

+

+	/**

+	 * @return the type

+	 */

+	public String getType() {

+		return type;

+	}

+

+	/**

+	 * @param type

+	 *            the type to set

+	 */

+	public void setType(String type) {

+		this.type = type;

+	}

+

+	/**

+	 * @return the id

+	 */

+	public String getId() {

+		return id;

+	}

+

+	/**

+	 * @param id

+	 *            the id to set

+	 */

+	public void setId(String id) {

+		this.id = id;

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/ShoppingBasket.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/ShoppingBasket.java
new file mode 100644
index 0000000..b3537ce
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/ShoppingBasket.java
@@ -0,0 +1,68 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.shoppingbasket.entity;

+

+import java.util.List;

+

+import javax.xml.bind.annotation.XmlElement;

+import javax.xml.bind.annotation.XmlElementWrapper;

+import javax.xml.bind.annotation.XmlRootElement;

+import javax.xml.bind.annotation.XmlType;

+

+/**

+ * ShoppingBasket contains a list of item and an optional name.

+ *

+ */

+@XmlRootElement(name = "shoppingbasket")

+@XmlType(propOrder = { "name", "items" })

+public class ShoppingBasket {

+	private String name;

+

+	private List<BasketItem> items;

+

+	/**

+	 * @return the name

+	 */

+	@XmlElement(name = "name")

+	public String getName() {

+		return name;

+	}

+

+	/**

+	 * @param name

+	 *            the name to set

+	 */

+	public void setName(String name) {

+		this.name = name;

+	}

+

+	/**

+	 * @return the items

+	 */

+

+	@XmlElementWrapper(name = "items")

+	@XmlElement(name = "item")

+	public List<BasketItem> getItems() {

+		return items;

+	}

+

+	/**

+	 * @param items

+	 *            the items to set

+	 */

+	public void setItems(List<BasketItem> items) {

+		this.items = items;

+	}

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/ShoppingBasketRequest.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/ShoppingBasketRequest.java
new file mode 100644
index 0000000..69168bc
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/ShoppingBasketRequest.java
@@ -0,0 +1,63 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.shoppingbasket.entity;

+

+import java.util.Collections;

+import java.util.List;

+

+/**

+ * ShoppingBasketRequest represents a client request to generate a shopping

+ * basket.

+ *

+ */

+public class ShoppingBasketRequest {

+	private String name;

+	private List<MDMItem> items;

+

+	/**

+	 * @return the name

+	 */

+	public String getName() {

+		return name;

+	}

+

+	/**

+	 * @param name

+	 *            the name to set

+	 */

+	public void setName(String name) {

+		this.name = name;

+	}

+

+	/**

+	 * @return the items

+	 */

+	public List<MDMItem> getItems() {

+		return items;

+	}

+

+	/**

+	 * @param items

+	 *            the items to set

+	 */

+	public void setItems(List<MDMItem> items) {

+		if (items == null) {

+			this.items = Collections.emptyList();

+		} else {

+			this.items = items;

+		}

+	}

+

+}

diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/package-info.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/package-info.java
new file mode 100644
index 0000000..57576b5
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/shoppingbasket/entity/package-info.java
@@ -0,0 +1,22 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+@XmlSchema(namespace = "http://www.eclipse/openmdm/shoppingbasket/1.0/",

+xmlns = { 

+    @XmlNs(prefix = "", namespaceURI = "http://www.eclipse/openmdm/shoppingbasket/1.0/"),

+}, elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)

+package org.eclipse.mdm.shoppingbasket.entity;

+

+import javax.xml.bind.annotation.XmlNs;

+import javax.xml.bind.annotation.XmlSchema;

diff --git a/org.eclipse.mdm.businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization.properties b/org.eclipse.mdm.businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization.properties
new file mode 100644
index 0000000..3d78479
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization.properties
@@ -0,0 +1,63 @@
+/********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+ 
+Environment = Environment
+Environment.Name = Name
+Environment.MimeType = MimeType
+Environment.Description = Description
+Environment.DateCreated = DateCreated
+Environment.Max_test_level = MaxTestLevels
+Environment.Base_model_version = BaseModelVersion
+Environment.AppModelType = ApplicationModelType
+Environment.AppModelVersion = ApplicationModelVersion
+Environment.Timezone = Timezone
+
+Test = Test
+Test.Name = Name
+Test.MimeType = MimeType
+Test.Description = Description
+Test.DateCreated = DateCreated
+Test.DateClosed = DateClosed
+Test.MDMLinks = Links
+
+TestStep = TestStep
+TestStep.Name = Name
+TestStep.MimeType = MimeType
+TestStep.Description = Description
+TestStep.DateCreated = DateCreated
+TestStep.Optional = Optional
+TestStep.MDMLinks = Links
+
+Measurement = Measurement
+Measurement.Name = Name
+Measurement.MimeType = MimeType
+Measurement.Description = Description
+Measurement.DateCreated = DateCreated
+Measurement.MeasurementBegin = Begin
+Measurement.MeasurementEnd = End
+Measurement.MDMLinks = Links
+
+ChannelGroup = ChannelGroup
+ChannelGroup.Name = Name
+ChannelGroup.MimeType = MimeType
+ChannelGroup.SubMatrixNoRows = Rows
+
+Channel = Channel
+Channel.Name = Name
+Channel.Description = MimeType
+Channel.DataType = DataType
+Channel.Minimum = Minimum
+Channel.Maximum = Maximum
+Channel.Average = Average
+Channel.Deviation = Deviation
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization_de.properties b/org.eclipse.mdm.businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization_de.properties
new file mode 100644
index 0000000..27150c6
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/resources/org/eclipse/mdm/businessobjects/control/i18n/locale/localization_de.properties
@@ -0,0 +1,64 @@
+/********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
+
+
+Environment = Datenquelle
+Environment.Name = Name
+Environment.MimeType = MimeType
+Environment.Description = Beschreibung
+Environment.DateCreated = Angelegt am:
+Environment.Max_test_level = Testlevels
+Environment.Base_model_version = Basismodell Version
+Environment.AppModelType = Applicationmodell Typ
+Environment.AppModelVersion = Applikationsmodell Version
+Environment.Timezone = Zeitzone
+
+Test = Versuch
+Test.Name = Name
+Test.MimeType = MimeType
+Test.Description = Beschreibung
+Test.DateCreated = Angelegt am:
+Test.DateClosed = Geschlossen am:
+Test.MDMLinks = Links
+
+TestStep = Versuchsschritt
+TestStep.Name = Name
+TestStep.MimeType = MimeType
+TestStep.Description = Beschreibung
+TestStep.DateCreated = Angelegt am:
+TestStep.Optional = Optional
+TestStep.MDMLinks = Links
+
+MeaResult = Messung
+MeaResult.Name = Name
+MeaResult.MimeType = MimeType
+MeaResult.Description = Beschreibung
+MeaResult.DateCreated = Angelegt am:
+MeaResult.MeasurementBegin = Messungsbeginn
+MeaResult.MeasurementEnd = Messungsende
+MeaResult.MDMLinks = Links
+
+SubMatrix = Kanalgruppe
+SubMatrix.Name = Name
+SubMatrix.MimeType = MimeType
+SubMatrix.SubMatrixNoRows = Messpunkte pro Kanal
+
+MeaQuantity = Kanal
+MeaQuantity.Name = Name
+MeaQuantity.Description = MimeType
+MeaQuantity.DataType = Datentyp
+MeaQuantity.Minimum = Minimum
+MeaQuantity.Maximum = Maximum
+MeaQuantity.Average = Durchschnitt
+MeaQuantity.Deviation = Standardabweichung
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/resources/shoppingbasket1.0.xsd b/org.eclipse.mdm.businessobjects/src/main/resources/shoppingbasket1.0.xsd
new file mode 100644
index 0000000..3e8211b
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/main/resources/shoppingbasket1.0.xsd
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>

+<!--********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 

+	targetNamespace="http://www.eclipse/openmdm/shoppingbasket/1.0/"

+	xmlns="http://www.eclipse/openmdm/shoppingbasket/1.0/"

+	elementFormDefault="qualified">

+	

+  <xs:element name="shoppingbasket" type="shoppingbasketType"/>

+  <xs:complexType name="itemType">

+    <xs:all>

+      <xs:element type="xs:string" name="source" minOccurs="1" maxOccurs="1"/>

+      <xs:element type="xs:anyURI" name="resturi" minOccurs="1" maxOccurs="1"/>

+      <xs:element type="xs:string" name="link" minOccurs="1" maxOccurs="1"/>

+    </xs:all>

+  </xs:complexType>

+  <xs:complexType name="itemsType">

+    <xs:sequence>

+      <xs:element type="itemType" name="item" minOccurs="0" maxOccurs="unbounded"/>

+    </xs:sequence>

+  </xs:complexType>

+  <xs:complexType name="shoppingbasketType">

+    <xs:all>

+      <xs:element type="xs:string" name="name" minOccurs="0" maxOccurs="1"/>

+      <xs:element type="itemsType" name="items" minOccurs="1" maxOccurs="1"/>

+    </xs:all>

+  </xs:complexType>

+</xs:schema>

+

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..bd8e6dc
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/PoolServiceTest.java
@@ -0,0 +1,189 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+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.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.adapter.ModelManager;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+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 {
+
+	ApplicationContext context = Mockito.mock(ApplicationContext.class);
+	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.getContextByName("MDMTEST")).thenReturn(context);
+		when(context.getEntityManager()).thenReturn(Optional.of(em));
+		when(context.getModelManager()).thenReturn(Optional.of(mm));
+	}
+
+	@Test
+	public void testGetPool() throws DataAccessException {
+		service.getPool("MDMTEST", "1");
+
+		verify(em).load(Pool.class, "1");
+		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).getContextByName("wrongEnvironment");
+
+		service.getPools("wrongEnvironment", "Pool.Name eq 'crash'");
+	}
+
+	@Test
+	public void testGetPoolsParentFilter() {
+		service.getPools("MDMTEST", "Project.Id eq '4711'");
+
+		verify(navigationActivity).getPools("MDMTEST", "4711");
+		verifyZeroInteractions(searchActivity);
+	}
+
+	@Test
+	public void testGetPoolsNoParentFilter() {
+		service.getPools("MDMTEST", "Project.Id eq '4711' or Project.Name eq 'Project A'");
+
+		verify(searchActivity).search(context, Pool.class, "Project.Id eq '4711' or Project.Name eq 'Project A'");
+		verifyZeroInteractions(navigationActivity);
+	}
+	
+	@Test
+	public void testGetPools() {
+		service.getPools("MDMTEST", "Pool.Name eq 'crash'");
+
+		verify(searchActivity).search(context, Pool.class, "Pool.Name eq 'crash'");
+	}
+
+	@Test
+	public void testGetSearchAttributes() {
+		service.getSearchAttributes("MDMTEST");
+
+		verify(searchActivity).listAvailableAttributes(context, 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");
+		when(projectId.getValueType()).thenReturn(ValueType.STRING);
+		when(projectId.createValue(any(), any())).thenCallRealMethod();
+		when(projectId.createValue(any(), anyBoolean(), any())).thenCallRealMethod();
+		
+		Attribute projectName = mock(Attribute.class);
+		when(projectName.getName()).thenReturn("Name");
+		when(projectName.getValueType()).thenReturn(ValueType.STRING);
+		when(projectName.createValue(any(), any())).thenCallRealMethod();
+		when(projectName.createValue(any(), anyBoolean(), any())).thenCallRealMethod();
+		
+		EntityType project = mock(EntityType.class);
+		when(project.getSourceName()).thenReturn("MDMTEST");
+		when(project.getName()).thenReturn("Project");
+		when(project.getAttributes()).thenReturn(Arrays.asList(projectId));
+		when(project.getAttribute("Id")).thenReturn(projectId);
+		when(project.getIDAttribute()).thenReturn(projectId);
+		when(project.getAttribute("Name")).thenReturn(projectName);
+		when(project.getNameAttribute()).thenReturn(projectName);
+
+		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 mmgr = mock(ModelManager.class);
+		when(mmgr.listEntityTypes()).thenReturn(Arrays.asList(project, pool));
+		when(mmgr.getEntityType(Project.class)).thenReturn(project);
+		when(mmgr.getEntityType("Project")).thenReturn(project);
+		when(mmgr.getEntityType(Pool.class)).thenReturn(pool);
+		when(mmgr.getEntityType("Pool")).thenReturn(pool);
+
+		return mmgr;
+	}
+}
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..fede525
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/ProjectServiceTest.java
@@ -0,0 +1,112 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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 java.util.Optional;
+
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+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.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 ProjectServiceTest {
+
+	ApplicationContext context = Mockito.mock(ApplicationContext.class);
+	EntityManager em = Mockito.mock(EntityManager.class);
+	ConnectorService connectorService = Mockito.mock(ConnectorService.class);
+	SearchActivity searchActivity = Mockito.mock(SearchActivity.class);
+	NavigationActivity navigationActivity = Mockito.mock(NavigationActivity.class);
+	I18NActivity i18nActivity = Mockito.mock(I18NActivity.class);
+
+	ProjectService service = new ProjectService(connectorService, searchActivity, i18nActivity);
+
+	@Before
+	public void init() {
+		when(context.getEntityManager()).thenReturn(Optional.of(em));
+		when(connectorService.getContextByName("MDMTEST")).thenReturn(context);
+	}
+
+	@Test
+	public void testGetProject() throws DataAccessException {
+		service.getProject("MDMTEST", "1");
+
+		verify(em).load(Project.class, "1");
+		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).getContextByName("wrongEnvironment");
+
+		service.getProjects("wrongEnvironment", "Project.Name eq crash");
+	}
+
+	@Test
+	public void testGetProjects() {
+		service.getProjects("MDMTEST", "Project.Name eq crash");
+
+		verify(searchActivity).search(context, Project.class, "Project.Name eq crash");
+	}
+
+	@Test
+	public void testGetSearchAttributes() {
+		service.getSearchAttributes("MDMTEST");
+
+		verify(searchActivity).listAvailableAttributes(context, 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/businessobjects/boundary/integrationtest/CatalogAttributeTEQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogAttributeTEQResourceIntegrationTest.java
new file mode 100644
index 0000000..4c75958
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogAttributeTEQResourceIntegrationTest.java
@@ -0,0 +1,74 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for CatalogAttributeResource for TestEquipment
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class CatalogAttributeTEQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing CatalogAttributeTEQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentTEQResourceIntegrationTest.prepareTestData();
+		CatalogComponentTEQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the ValueList to reference
+		ValueListResourceIntegrationTest.prepareTestData();
+		ValueListResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(CatalogAttributeTEQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/catcomps/testequipment/"
+				+ getTestDataValue(CatalogComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/catattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testCatAttrTEQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "CatalogAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_DATATYPE, new JsonPrimitive("STRING"));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		json.add("ValueList",
+				new JsonPrimitive(getTestDataValue(ValueListResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+		CatalogComponentTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(ValueListResourceIntegrationTest.class);
+		ValueListResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogAttributeTSQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogAttributeTSQResourceIntegrationTest.java
new file mode 100644
index 0000000..874595d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogAttributeTSQResourceIntegrationTest.java
@@ -0,0 +1,72 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for CatalogAttributeResource for TestSequence {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class CatalogAttributeTSQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing CatalogAttributeTSQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentTSQResourceIntegrationTest.prepareTestData();
+		CatalogComponentTSQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the ValueList to reference
+		ValueListResourceIntegrationTest.prepareTestData();
+		ValueListResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(CatalogAttributeTSQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/catcomps/testsequence/" + getTestDataValue(CatalogComponentTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/catattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testCatAttrTSQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "CatalogAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_DATATYPE, new JsonPrimitive("STRING"));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		json.add("ValueList",
+				new JsonPrimitive(getTestDataValue(ValueListResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(CatalogComponentTSQResourceIntegrationTest.class);
+		CatalogComponentTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(ValueListResourceIntegrationTest.class);
+		ValueListResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogAttributeUUTResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogAttributeUUTResourceIntegrationTest.java
new file mode 100644
index 0000000..ce0629d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogAttributeUUTResourceIntegrationTest.java
@@ -0,0 +1,73 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for CatalogAttributeResource for UnitUnderTest
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class CatalogAttributeUUTResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing CatalogAttributeUUTResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentUUTResourceIntegrationTest.prepareTestData();
+		CatalogComponentUUTResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the ValueList to reference
+		ValueListResourceIntegrationTest.prepareTestData();
+		ValueListResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(CatalogAttributeUUTResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/catcomps/unitundertest/" + getTestDataValue(CatalogComponentUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/catattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testCatAttrUUT");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "CatalogAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_DATATYPE, new JsonPrimitive("STRING"));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		json.add("ValueList",
+				new JsonPrimitive(getTestDataValue(ValueListResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(CatalogComponentUUTResourceIntegrationTest.class);
+		CatalogComponentUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(ValueListResourceIntegrationTest.class);
+		ValueListResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogComponentTEQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogComponentTEQResourceIntegrationTest.java
new file mode 100644
index 0000000..273af52
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogComponentTEQResourceIntegrationTest.java
@@ -0,0 +1,49 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for CatalogComponentResource for TestEquipment
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class CatalogComponentTEQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing CatalogComponentTEQResourceIntegrationTest");
+
+		// set up test data
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/catcomps/testequipment");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testCatCompTEQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "CatalogComponent");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogComponentTSQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogComponentTSQResourceIntegrationTest.java
new file mode 100644
index 0000000..18befd5
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogComponentTSQResourceIntegrationTest.java
@@ -0,0 +1,48 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for CatalogComponentResource for TestSequence {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class CatalogComponentTSQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing CatalogComponentTSQResourceIntegrationTest");
+
+		// set up test data
+		setContextClass(CatalogComponentTSQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/catcomps/testsequence");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testCatCompTSQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "CatalogComponent");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogComponentUUTResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogComponentUUTResourceIntegrationTest.java
new file mode 100644
index 0000000..faca92b
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogComponentUUTResourceIntegrationTest.java
@@ -0,0 +1,49 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for CatalogComponentResource for UnitUnderTest
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class CatalogComponentUUTResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing CatalogComponentUUTResourceIntegrationTest");
+
+		// set up test data
+		setContextClass(CatalogComponentUUTResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/catcomps/unitundertest");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testCatCompUUT");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "CatalogComponent");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogSensorAttributeResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogSensorAttributeResourceIntegrationTest.java
new file mode 100644
index 0000000..5219fb9
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogSensorAttributeResourceIntegrationTest.java
@@ -0,0 +1,65 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for CatalogSensorAttributeResource
+ * 
+ * @author Philipp Schweinbenz, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class CatalogSensorAttributeResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing CatalogSensorAttributeResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogSensor
+		CatalogSensorResourceIntegrationTest.prepareTestData();
+		CatalogSensorResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(CatalogSensorAttributeResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/catcomps/testequipment/"
+				+ getTestDataValue(CatalogComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID)
+				+ "/catsensors/" + getTestDataValue(CatalogSensorResourceIntegrationTest.class, TESTDATA_ENTITY_ID)
+				+ "/catsensorattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testCatSensorAttr");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "CatalogAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_DATATYPE, new JsonPrimitive("STRING"));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(CatalogSensorResourceIntegrationTest.class);
+		CatalogSensorResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+		CatalogComponentTEQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogSensorResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogSensorResourceIntegrationTest.java
new file mode 100755
index 0000000..ed608db
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/CatalogSensorResourceIntegrationTest.java
@@ -0,0 +1,58 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for CatalogSensorResource
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class CatalogSensorResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing CatalogSensorResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentTEQResourceIntegrationTest.prepareTestData();
+		CatalogComponentTEQResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(CatalogSensorResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/catcomps/testequipment/" + getTestDataValue(CatalogComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/catsensors");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testCatSensor");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "CatalogSensor");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+		CatalogComponentTEQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/EntityResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/EntityResourceIntegrationTest.java
new file mode 100644
index 0000000..7901961
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/EntityResourceIntegrationTest.java
@@ -0,0 +1,478 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import java.util.NoSuchElementException;
+
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+
+import io.restassured.RestAssured;
+import io.restassured.authentication.PreemptiveBasicAuthScheme;
+import io.restassured.http.ContentType;
+import io.restassured.response.ExtractableResponse;
+import io.restassured.response.Response;
+import io.vavr.collection.HashMap;
+import io.vavr.collection.HashSet;
+import io.vavr.collection.Map;
+import io.vavr.collection.Set;
+
+/**
+ * Abstract test class for Entity resources. Tests are executed in
+ * {@code FixMethodOrder(MethodSorters.NAME_ASCENDING)} as {@link test1Create},
+ * {@link test2Find()}, {@link test3FindAll()}, {@link test4Update()} and
+ * {@link test5Delete()} depend on the entity created by test1Create().
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ *
+ */
+// TODO anehmer on 2017-11-23: test for specific return codes
+// TODO anehmer on 2017-11-24: expand tests to localization and search attribute
+// information
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public abstract class EntityResourceIntegrationTest {
+
+	private static Logger LOGGER = LoggerFactory.getLogger(EntityResourceIntegrationTest.class);
+
+	private static final String HOST = "localhost";
+	private static final String PORT = "8080";
+	private static final String BASE_PATH = "org.eclipse.mdm.nucleus";
+	private static final String API_PATH = "mdm";
+	private static final String ENV_PATH = "environments/PODS";
+
+	private static final String AUTH_USERNAME = "sa";
+	private static final String AUTH_PASSWORD = "sa";
+
+	protected final static String TESTDATA_ENTITY_ID = "entityId";
+	protected final static String TESTDATA_ENTITY_NAME = "entityName";
+	protected final static String TESTDATA_ENTITY_TYPE = "entityType";
+	protected final static String TESTDATA_CREATE_JSON_BODY = "createJSONBody";
+	protected final static String TESTDATA_UPDATE_JSON_BODY = "updateJSONBody";
+	protected final static String TESTDATA_RESOURCE_URI = "resourceURI";
+	protected final static String TESTDATA_RANDOM_DATA = "RANDOM_DATA";
+
+	private static final String RANDOM_ENTITY_NAME_SUFFIX = "_" + Long.toHexString(System.currentTimeMillis());
+
+	public enum TestType {
+		CREATE, FIND, FINDALL, UPDATE, DELETE;
+	}
+
+	private static Map<Class<?>, Set<TestType>> testsToSkip = HashMap.empty();
+
+	private static Map<Class<?>, Map<String, String>> testDataMap = HashMap.empty();
+
+	// if this is set, the resource URI for find() is constructed with the name as
+	// the PATH_PARAM
+	private static Map<Class<?>, Boolean> findByName = HashMap.empty();
+
+	/**
+	 * The context class must be set by implementing tests as the context to get
+	 * from and put test data values to
+	 */
+	private static Class<?> contextClass;
+
+	/**
+	 * Init RestAssured
+	 */
+	static {
+		// configure URI
+		StringBuilder baseURI = new StringBuilder();
+		baseURI.append("http://")
+				.append(HOST)
+				.append(":")
+				.append(PORT)
+				.append("/")
+				.append(BASE_PATH)
+				.append("/")
+				.append(API_PATH);
+		RestAssured.baseURI = baseURI.toString();
+		RestAssured.basePath = ENV_PATH;
+
+		LOGGER.debug("RestAssured set up to " + RestAssured.baseURI + "/" + RestAssured.basePath);
+
+		// setup authentication
+		PreemptiveBasicAuthScheme authScheme = new PreemptiveBasicAuthScheme();
+		authScheme.setUserName(AUTH_USERNAME);
+		authScheme.setPassword(AUTH_PASSWORD);
+
+		RestAssured.authentication = authScheme;
+
+		LOGGER.debug("RestAssured authentication set to credentials [" + AUTH_USERNAME + "]/[" + AUTH_PASSWORD + "]");
+	}
+
+	@Test
+	public void test1Create() {
+		// only execute if not skipped by implementing test class
+		Assume.assumeFalse(testsToSkip.get(getContextClass())
+				.get()
+				.contains(TestType.CREATE));
+
+		createEntity();
+	}
+
+	/**
+	 * Static method that can be utilised by tests to create a specific entity or is
+	 * called indirectly by JUnit
+	 */
+	public static void createEntity() {
+		// do not create entity if it was already created in a currently running
+		// prepareTestData() cascade
+		if (isTestDataValuePresent(TESTDATA_ENTITY_ID)) {
+			LOGGER.debug(getContextClass().getSimpleName() + ".create() aborted as entity "
+					+ getTestDataValue(TESTDATA_ENTITY_NAME) + " of type " + getTestDataValue(TESTDATA_ENTITY_TYPE)
+					+ " was already created");
+			return;
+		}
+
+		LOGGER.debug(getContextClass().getSimpleName() + ".create() sending POST to "
+				+ getTestDataValue(TESTDATA_RESOURCE_URI) + " with: " + getTestDataValue(TESTDATA_CREATE_JSON_BODY));
+
+		ExtractableResponse<io.restassured.response.Response> response = given().contentType(ContentType.JSON)
+				.body(getTestDataValue(TESTDATA_CREATE_JSON_BODY))
+				.post(getTestDataValue(TESTDATA_RESOURCE_URI))
+				.then()
+				.log()
+				.ifError()
+				.contentType(ContentType.JSON)
+				// do not check for name equality as that might be created randomly
+				.and()
+				.body("data.first().type", equalTo(getTestDataValue(TESTDATA_ENTITY_TYPE)))
+				.extract();
+
+		LOGGER.debug(getContextClass().getSimpleName() + " created " + response.asString());
+
+		putTestDataValue(TESTDATA_ENTITY_ID, response.path("data.first().id"));
+		putTestDataValue(TESTDATA_ENTITY_NAME, response.path("data.first().name"));
+	}
+
+	@Test
+	public void test2Find() {
+		// only execute if not skipped by implementing test class
+		Assume.assumeFalse(testsToSkip.get(getContextClass())
+				.get()
+				.contains(TestType.FIND));
+
+		String uri = getTestDataValue(TESTDATA_RESOURCE_URI) + "/"
+				+ (EntityResourceIntegrationTest.findByName.getOrElse(getContextClass(), false)
+						? getTestDataValue(TESTDATA_ENTITY_NAME)
+						: getTestDataValue(TESTDATA_ENTITY_ID));
+
+		LOGGER.debug(getContextClass().getSimpleName() + ".find() sending GET to " + uri);
+
+		ExtractableResponse<Response> response = given().get(uri)
+				.then()
+				.log()
+				.ifError()
+				.contentType(ContentType.JSON)
+				.body("data.first().name", equalTo(getTestDataValue(TESTDATA_ENTITY_NAME)))
+				.body("data.first().type", equalTo(getTestDataValue(TESTDATA_ENTITY_TYPE)))
+				.extract();
+
+		LOGGER.debug(getContextClass().getSimpleName() + " found " + response.asString());
+	}
+
+	/**
+	 * Finds the first entity of the {@code EntityType} set in the context and put
+	 * the found ID in the context for further usage
+	 */
+	public static void findFirst() {
+		LOGGER.debug(getContextClass().getSimpleName() + ".find() sending GET to "
+				+ getTestDataValue(TESTDATA_RESOURCE_URI));
+
+		String id = given().get(getTestDataValue(TESTDATA_RESOURCE_URI))
+				.then()
+				.log()
+				.ifError()
+				.contentType(ContentType.JSON)
+				.body("data.first().name", equalTo(getTestDataValue(TESTDATA_ENTITY_NAME)))
+				.body("data.first().type", equalTo(getTestDataValue(TESTDATA_ENTITY_TYPE)))
+				.extract()
+				.path("data.first().id");
+
+		LOGGER.debug(getContextClass().getSimpleName() + " found " + getTestDataValue(TESTDATA_ENTITY_TYPE)
+				+ " with ID " + id);
+
+		putTestDataValue(TESTDATA_ENTITY_ID, id);
+	}
+
+	@Test
+	public void test3FindAll() {
+		// only execute if not skipped by implementing test class
+		Assume.assumeFalse(testsToSkip.get(getContextClass())
+				.get()
+				.contains(TestType.FINDALL));
+
+		LOGGER.debug(getContextClass().getSimpleName() + ".findAll() sending GET to "
+				+ getTestDataValue(TESTDATA_RESOURCE_URI));
+
+		ExtractableResponse<Response> response = given().get(getTestDataValue(TESTDATA_RESOURCE_URI))
+				.then()
+				.log()
+				.ifError()
+				.contentType(ContentType.JSON)
+				.body("data.first().type", equalTo(getTestDataValue(TESTDATA_ENTITY_TYPE)))
+				.extract();
+
+		LOGGER.debug(getContextClass().getSimpleName() + " found all " + response.asString());
+	}
+
+	// TODO anehmer on 2017-11-09: test findAll with filter
+
+	// TODO anehmer on 2018-02-06: update of relations are not checked as the
+	// returned Json does not include relations
+	@Test
+	public void test4Update() {
+		// only execute if not skipped by implementing test class
+		Assume.assumeFalse(testsToSkip.get(getContextClass())
+				.get()
+				.contains(TestType.UPDATE));
+
+		JsonObject json;
+		// if no UPDATE_JSON_BODY is defined in implementing test, just run the MimeType
+		// update
+		if (!isTestDataValuePresent(TESTDATA_UPDATE_JSON_BODY)) {
+			json = new JsonObject();
+		}
+		// or add it to the existing update
+		else {
+			json = new JsonParser().parse(getTestDataValue(TESTDATA_UPDATE_JSON_BODY))
+					.getAsJsonObject();
+		}
+		json.add("MimeType", new JsonPrimitive("updatedMimeType"));
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+
+		String uri = getTestDataValue(TESTDATA_RESOURCE_URI) + "/" + getTestDataValue(TESTDATA_ENTITY_ID);
+
+		LOGGER.debug(getContextClass().getSimpleName() + ".update() sending PUT to " + uri + " with: "
+				+ getTestDataValue(TESTDATA_UPDATE_JSON_BODY));
+
+		ExtractableResponse<Response> response = given().contentType(ContentType.JSON)
+				// TODO anehmer on 2017-11-15: the update should use different data but as the
+				// returned JSON represents
+				// the entity prior update it does not make any difference as the update is
+				// performed just based on identical data. We should discuss the PUT-behaviour
+				// instead: return the old or updated object as returning the updated one would
+				// mean to perform another get as the ODSTransaction.update() does not return
+				// the updated entity
+				// TODO anehmer on 2017-11-15: use Description to test update
+				.body(getTestDataValue(TESTDATA_UPDATE_JSON_BODY))
+				.put(uri)
+				.then()
+				.log()
+				.ifError()
+				.contentType(ContentType.JSON)
+				.body("data.first().name", equalTo(getTestDataValue(TESTDATA_ENTITY_NAME)))
+				.body("data.first().type", equalTo(getTestDataValue(TESTDATA_ENTITY_TYPE)))
+				.body("data.first().attributes.find {it.name == 'MimeType'}.value", equalTo("updatedMimeType"))
+				.extract();
+
+		LOGGER.debug(getContextClass().getSimpleName() + " updated " + response.asString());
+	}
+
+	@Test
+	public void test5Delete() {
+		// only execute if not skipped by implementing test class
+		Assume.assumeFalse(testsToSkip.get(getContextClass())
+				.get()
+				.contains(TestType.DELETE));
+
+		deleteEntity();
+	}
+
+	/**
+	 * Static method that can be utilised by tests to delete a specific entity or is
+	 * called indirectly by JUnit
+	 */
+	public static void deleteEntity() {
+		String uri = getTestDataValue(TESTDATA_RESOURCE_URI) + "/" + getTestDataValue(TESTDATA_ENTITY_ID);
+
+		LOGGER.debug(getContextClass().getSimpleName() + ".delete() sending DELETE to " + uri);
+
+		ExtractableResponse<Response> response = given().delete(uri)
+				.then()
+				.log()
+				.ifError()
+				.body("data.first().name", equalTo(getTestDataValue(TESTDATA_ENTITY_NAME)))
+				.body("data.first().type", equalTo(getTestDataValue(TESTDATA_ENTITY_TYPE)))
+				.extract();
+
+		LOGGER.debug(getContextClass().getSimpleName() + " deleted " + response.asString());
+
+		removeTestDataValue(TESTDATA_ENTITY_ID);
+	}
+
+	/**
+	 * Gets value with key from the testDataMap. The value map is thereby
+	 * automatically identified by the implementing class.
+	 * 
+	 * @param key
+	 *            key to get value for
+	 * @return value for given key
+	 */
+	public static String getTestDataValue(String key) {
+		return getTestDataValue(getContextClass(), key);
+	}
+
+	/**
+	 * Gets value with key from the testDataMap using the context specified by
+	 * contextClass
+	 * 
+	 * @param contextClass
+	 *            the class of the test implementation
+	 * @param key
+	 *            key to get value for
+	 * @return value for given key
+	 */
+	public static String getTestDataValue(Class<?> contextClass, String key) {
+		return testDataMap.get(contextClass)
+				.map(valueMap -> valueMap.get(key)
+						.getOrElseThrow(() -> new NoSuchElementException(
+								"Key [" + key + "] not found in test data value map in context ["
+										+ contextClass.getSimpleName() + "]")))
+				.get();
+	}
+
+	/**
+	 * Checks if a test data value is present for the given key
+	 * 
+	 * @param key
+	 *            key to check presence of test data value for
+	 * @return true, if a test data value for the given key exists, false if not
+	 */
+	public static boolean isTestDataValuePresent(String key) {
+		return testDataMap.get(getContextClass())
+				.map(valueMap -> valueMap.get(key)
+						.isDefined())
+				.get();
+	}
+
+	/**
+	 * Removes the test data value for the given key. If the key is not present,
+	 * nothing happens.
+	 * 
+	 * @param key
+	 *            key to remove test data value for
+	 */
+	public static void removeTestDataValue(String key) {
+		testDataMap.get(getContextClass())
+				.map(valueMap -> valueMap.remove(key))
+				.map(newValueMap -> testDataMap = testDataMap.put(getContextClass(), newValueMap));
+	}
+
+	/**
+	 * Puts value with key in the testDataMap. The value map is thereby
+	 * automatically identified by the implementing class.
+	 * 
+	 * @param key
+	 *            key to store value under
+	 * @param value
+	 *            value to store
+	 */
+	public static void putTestDataValue(String key, String value) {
+		Map<String, String> entityTestData = testDataMap.getOrElse(getContextClass(), HashMap.empty());
+
+		// randomize name to allow failure runs not to require to reset the
+		// database in case the name of the entity must be unique
+		// do not append suffix if name is randomly generated
+		if (key.equals(TESTDATA_ENTITY_NAME) && !value.equals(TESTDATA_RANDOM_DATA)
+				&& (entityTestData.get(TESTDATA_ENTITY_NAME)
+						.isEmpty()
+						|| !entityTestData.get(TESTDATA_ENTITY_NAME)
+								.get()
+								.equals(TESTDATA_RANDOM_DATA))) {
+			// append suffix if it was not already appended or an already suffixed value was
+			// used for a new one (e.g: TplAttr.name and CatAttr.name)
+			if (!value.endsWith(RANDOM_ENTITY_NAME_SUFFIX)) {
+				value = value + RANDOM_ENTITY_NAME_SUFFIX;
+			}
+		}
+
+		entityTestData = entityTestData.put(key, value);
+
+		testDataMap = testDataMap.put(getContextClass(), entityTestData);
+	}
+
+	/**
+	 * Gets the context class set by a test implementation used to store context
+	 * aware test data
+	 * 
+	 * @return the context class of the test implementation
+	 */
+	private static Class<?> getContextClass() {
+		assertThat(contextClass, is(notNullValue()));
+		return contextClass;
+	}
+
+	/**
+	 * Sets the context class used to store context aware test data. This method
+	 * must be called by any test implementation before using
+	 * {@link getTestDataValue} or {@link putTestDataValue}
+	 * 
+	 * @param contextClass
+	 *            the context class set by a test implementation
+	 */
+	public static void setContextClass(Class<?> contextClass) {
+		EntityResourceIntegrationTest.contextClass = contextClass;
+		testsToSkip = testsToSkip.put(contextClass, HashSet.empty());
+		LOGGER = LoggerFactory.getLogger(contextClass);
+	}
+
+	/**
+	 * Set the test with the given {@link TestType} to be skipped
+	 * 
+	 * @param testType
+	 *            the test to skip
+	 */
+	public static void skipTest(TestType test) {
+		testsToSkip.get(getContextClass())
+				.map(tests -> tests.add(test))
+				.map(newTests -> testsToSkip = testsToSkip.put(getContextClass(), newTests));
+	}
+
+	/**
+	 * Sets the option findByName to either true or false. If it's set to true, the
+	 * URI for find() is constructed with the name rather than with the id of the
+	 * entity as the PATH_PARAM
+	 * 
+	 * @param findByName
+	 *            if find() should use the name instead of the id of the entity
+	 */
+	public static void setFindByName(boolean findByNameValue) {
+		findByName = findByName.put(getContextClass(), findByNameValue);
+	}
+
+	/**
+	 * Gets the logger
+	 * 
+	 * @return logger configured to current context class
+	 */
+	public static Logger getLogger() {
+		return LOGGER;
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateAttributeTEQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateAttributeTEQResourceIntegrationTest.java
new file mode 100644
index 0000000..3e1aba2
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateAttributeTEQResourceIntegrationTest.java
@@ -0,0 +1,81 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for NestedTemplateAttributeResource for TestEquipment
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class NestedTemplateAttributeTEQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing NestedTemplateAttributeTEQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogAttribute
+		CatalogAttributeTEQResourceIntegrationTest.prepareTestData();
+		CatalogAttributeTEQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the NestedTemplateComponent
+		NestedTemplateComponentTEQResourceIntegrationTest.prepareTestData();
+		NestedTemplateComponentTEQResourceIntegrationTest.createEntity();
+
+		setContextClass(NestedTemplateAttributeTEQResourceIntegrationTest.class);
+
+		// set up test data
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testequipment/" + getTestDataValue(TemplateRootTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(NestedTemplateComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, getTestDataValue(CatalogAttributeTEQResourceIntegrationTest.class, TESTDATA_ENTITY_NAME));
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(CatalogAttributeTEQResourceIntegrationTest.class, TESTDATA_ENTITY_NAME)));
+		json.add("catalogattribute", new JsonPrimitive(getTestDataValue(CatalogAttributeTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		// delete the implicitly created NestedTemplateAttribute
+		NestedTemplateAttributeTEQResourceIntegrationTest.findFirst();
+		NestedTemplateAttributeTEQResourceIntegrationTest.deleteEntity();
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(NestedTemplateComponentTEQResourceIntegrationTest.class);
+		NestedTemplateComponentTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateComponentTEQResourceIntegrationTest.class);
+		TemplateComponentTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootTEQResourceIntegrationTest.class);
+		TemplateRootTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogAttributeTEQResourceIntegrationTest.class);
+		CatalogAttributeTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+		CatalogComponentTEQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateAttributeTSQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateAttributeTSQResourceIntegrationTest.java
new file mode 100644
index 0000000..93df734
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateAttributeTSQResourceIntegrationTest.java
@@ -0,0 +1,80 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for NestedTemplateAttributeResource for TestSequence
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class NestedTemplateAttributeTSQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing NestedTemplateAttributeTSQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogAttribute
+		CatalogAttributeTSQResourceIntegrationTest.prepareTestData();
+		CatalogAttributeTSQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the NestedTemplateComponent
+		NestedTemplateComponentTSQResourceIntegrationTest.prepareTestData();
+		NestedTemplateComponentTSQResourceIntegrationTest.createEntity();
+
+		setContextClass(NestedTemplateAttributeTSQResourceIntegrationTest.class);
+
+		// set up test data
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testsequence/" + getTestDataValue(TemplateRootTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(NestedTemplateComponentTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, getTestDataValue(CatalogAttributeTSQResourceIntegrationTest.class, TESTDATA_ENTITY_NAME));
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(CatalogAttributeTSQResourceIntegrationTest.class, TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		// delete the implicitly created NestedTemplateAttribute
+		NestedTemplateAttributeTSQResourceIntegrationTest.findFirst();
+		NestedTemplateAttributeTSQResourceIntegrationTest.deleteEntity();
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(NestedTemplateComponentTSQResourceIntegrationTest.class);
+		NestedTemplateComponentTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateComponentTSQResourceIntegrationTest.class);
+		TemplateComponentTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootTSQResourceIntegrationTest.class);
+		TemplateRootTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogAttributeTSQResourceIntegrationTest.class);
+		CatalogAttributeTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTSQResourceIntegrationTest.class);
+		CatalogComponentTSQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateAttributeUUTResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateAttributeUUTResourceIntegrationTest.java
new file mode 100644
index 0000000..848481a
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateAttributeUUTResourceIntegrationTest.java
@@ -0,0 +1,80 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for NestedTemplateAttributeResource for UnitUnderTest
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class NestedTemplateAttributeUUTResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing NestedTemplateAttributeUUTResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogAttribute
+		CatalogAttributeUUTResourceIntegrationTest.prepareTestData();
+		CatalogAttributeUUTResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the NestedTemplateComponent
+		NestedTemplateComponentUUTResourceIntegrationTest.prepareTestData();
+		NestedTemplateComponentUUTResourceIntegrationTest.createEntity();
+
+		setContextClass(NestedTemplateAttributeUUTResourceIntegrationTest.class);
+
+		// set up test data
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/unitundertest/" + getTestDataValue(TemplateRootUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(NestedTemplateComponentUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, getTestDataValue(CatalogAttributeUUTResourceIntegrationTest.class, TESTDATA_ENTITY_NAME));
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(CatalogAttributeUUTResourceIntegrationTest.class, TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		// delete the implicitly created NestedTemplateAttribute
+		NestedTemplateAttributeUUTResourceIntegrationTest.findFirst();
+		NestedTemplateAttributeUUTResourceIntegrationTest.deleteEntity();
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(NestedTemplateComponentUUTResourceIntegrationTest.class);
+		NestedTemplateComponentUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateComponentUUTResourceIntegrationTest.class);
+		TemplateComponentUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootUUTResourceIntegrationTest.class);
+		TemplateRootUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogAttributeUUTResourceIntegrationTest.class);
+		CatalogAttributeUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentUUTResourceIntegrationTest.class);
+		CatalogComponentUUTResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateComponentTEQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateComponentTEQResourceIntegrationTest.java
new file mode 100644
index 0000000..e747491
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateComponentTEQResourceIntegrationTest.java
@@ -0,0 +1,71 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for NestedTemplateComponentResource for TestEquipment
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class NestedTemplateComponentTEQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing NestedTemplateComponentTEQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentTEQResourceIntegrationTest.prepareTestData();
+		CatalogComponentTEQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateRoot
+		TemplateComponentTEQResourceIntegrationTest.prepareTestData();
+		TemplateComponentTEQResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(NestedTemplateComponentTEQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testequipment/" + getTestDataValue(TemplateRootTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testNestedTplCompTEQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateComponent");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_CATALOGCOMPONENT_ID, new JsonPrimitive(getTestDataValue(CatalogComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateComponentTEQResourceIntegrationTest.class);
+		TemplateComponentTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootTEQResourceIntegrationTest.class);
+		TemplateRootTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+		CatalogComponentTEQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateComponentTSQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateComponentTSQResourceIntegrationTest.java
new file mode 100644
index 0000000..d2194c5
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateComponentTSQResourceIntegrationTest.java
@@ -0,0 +1,71 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for NestedTemplateComponentResource for TestSequence
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class NestedTemplateComponentTSQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing NestedTemplateComponentTSQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentTSQResourceIntegrationTest.prepareTestData();
+		CatalogComponentTSQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateRoot
+		TemplateComponentTSQResourceIntegrationTest.prepareTestData();
+		TemplateComponentTSQResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(NestedTemplateComponentTSQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testsequence/" + getTestDataValue(TemplateRootTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testNestedTplCompTSQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateComponent");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_CATALOGCOMPONENT_ID, new JsonPrimitive(getTestDataValue(CatalogComponentTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateComponentTSQResourceIntegrationTest.class);
+		TemplateComponentTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootTSQResourceIntegrationTest.class);
+		TemplateRootTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTSQResourceIntegrationTest.class);
+		CatalogComponentTSQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateComponentUUTResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateComponentUUTResourceIntegrationTest.java
new file mode 100644
index 0000000..a0823ca
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/NestedTemplateComponentUUTResourceIntegrationTest.java
@@ -0,0 +1,71 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for NestedTemplateComponentResource for UnitUnderTest
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class NestedTemplateComponentUUTResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing NestedTemplateComponentUUTResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentUUTResourceIntegrationTest.prepareTestData();
+		CatalogComponentUUTResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateRoot
+		TemplateComponentUUTResourceIntegrationTest.prepareTestData();
+		TemplateComponentUUTResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(NestedTemplateComponentUUTResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/unitundertest/" + getTestDataValue(TemplateRootUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testNestedTplCompUUT");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateComponent");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_CATALOGCOMPONENT_ID, new JsonPrimitive(getTestDataValue(CatalogComponentUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateComponentUUTResourceIntegrationTest.class);
+		TemplateComponentUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootUUTResourceIntegrationTest.class);
+		TemplateRootUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentUUTResourceIntegrationTest.class);
+		CatalogComponentUUTResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/PhysicalDimensionResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/PhysicalDimensionResourceIntegrationTest.java
new file mode 100644
index 0000000..801c20a
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/PhysicalDimensionResourceIntegrationTest.java
@@ -0,0 +1,51 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for PhysicalDimensionResource.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class PhysicalDimensionResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing PhysicalDimensionResourceIntegrationTest");
+
+		// set up test data
+		setContextClass(PhysicalDimensionResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/physicaldimensions");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testPhysicalDimension");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "PhysicalDimension");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		json.add("Mass", new JsonPrimitive(1));
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/QuantityResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/QuantityResourceIntegrationTest.java
new file mode 100755
index 0000000..91441ff
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/QuantityResourceIntegrationTest.java
@@ -0,0 +1,61 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for QuantityResource.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class QuantityResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing QuantityResourceIntegrationTest");
+
+		// prepare test data for creating the Unit
+		UnitResourceIntegrationTest.prepareTestData();
+		UnitResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(QuantityResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/quantities");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testQuantity");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "Quantity");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		// TODO anehmer on 2017-11-17: create unit (and physDim) instead of taking fixed
+		// one
+		json.add(ResourceConstants.ENTITYATTRIBUTE_UNIT_ID, new JsonPrimitive(getTestDataValue(UnitResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(UnitResourceIntegrationTest.class);
+		UnitResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateAttributeTEQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateAttributeTEQResourceIntegrationTest.java
new file mode 100644
index 0000000..fa3f345
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateAttributeTEQResourceIntegrationTest.java
@@ -0,0 +1,77 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateAttributeResource for TestEquipment
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateAttributeTEQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateAttributeTEQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogAttribute
+		CatalogAttributeTEQResourceIntegrationTest.prepareTestData();
+		CatalogAttributeTEQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateComponent
+		TemplateComponentTEQResourceIntegrationTest.prepareTestData();
+		TemplateComponentTEQResourceIntegrationTest.createEntity();
+
+		setContextClass(TemplateAttributeTEQResourceIntegrationTest.class);
+
+		// set up test data
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testequipment/" + getTestDataValue(TemplateRootTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, getTestDataValue(CatalogAttributeTEQResourceIntegrationTest.class, TESTDATA_ENTITY_NAME));
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(CatalogAttributeTEQResourceIntegrationTest.class, TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		// delete the implicitly created TemplateAttribute
+		TemplateAttributeTEQResourceIntegrationTest.findFirst();
+		TemplateAttributeTEQResourceIntegrationTest.deleteEntity();
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateComponentTEQResourceIntegrationTest.class);
+		TemplateComponentTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootTEQResourceIntegrationTest.class);
+		TemplateRootTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogAttributeTEQResourceIntegrationTest.class);
+		CatalogAttributeTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+		CatalogComponentTEQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateAttributeTSQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateAttributeTSQResourceIntegrationTest.java
new file mode 100644
index 0000000..353cd38
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateAttributeTSQResourceIntegrationTest.java
@@ -0,0 +1,77 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateAttributeResource for TestSequence
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateAttributeTSQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateAttributeTSQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogAttribute
+		CatalogAttributeTSQResourceIntegrationTest.prepareTestData();
+		CatalogAttributeTSQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateComponent
+		TemplateComponentTSQResourceIntegrationTest.prepareTestData();
+		TemplateComponentTSQResourceIntegrationTest.createEntity();
+
+		setContextClass(TemplateAttributeTSQResourceIntegrationTest.class);
+
+		// set up test data
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testsequence/" + getTestDataValue(TemplateRootTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, getTestDataValue(CatalogAttributeTSQResourceIntegrationTest.class, TESTDATA_ENTITY_NAME));
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(CatalogAttributeTSQResourceIntegrationTest.class, TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		// delete the implicitly created TemplateAttribute
+		TemplateAttributeTSQResourceIntegrationTest.findFirst();
+		TemplateAttributeTSQResourceIntegrationTest.deleteEntity();
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateComponentTSQResourceIntegrationTest.class);
+		TemplateComponentTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootTSQResourceIntegrationTest.class);
+		TemplateRootTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogAttributeTSQResourceIntegrationTest.class);
+		CatalogAttributeTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTSQResourceIntegrationTest.class);
+		CatalogComponentTSQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateAttributeUUTResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateAttributeUUTResourceIntegrationTest.java
new file mode 100644
index 0000000..6392d99
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateAttributeUUTResourceIntegrationTest.java
@@ -0,0 +1,77 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateAttributeResource for UnitUnderTest
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateAttributeUUTResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateAttributeUUTResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogAttribute
+		CatalogAttributeUUTResourceIntegrationTest.prepareTestData();
+		CatalogAttributeUUTResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateComponent
+		TemplateComponentUUTResourceIntegrationTest.prepareTestData();
+		TemplateComponentUUTResourceIntegrationTest.createEntity();
+
+		setContextClass(TemplateAttributeUUTResourceIntegrationTest.class);
+
+		// set up test data
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/unitundertest/" + getTestDataValue(TemplateRootUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME, getTestDataValue(CatalogAttributeUUTResourceIntegrationTest.class, TESTDATA_ENTITY_NAME));
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(CatalogAttributeUUTResourceIntegrationTest.class, TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		// delete the implicitly created TemplateAttribute
+		TemplateAttributeUUTResourceIntegrationTest.findFirst();
+		TemplateAttributeUUTResourceIntegrationTest.deleteEntity();
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateComponentUUTResourceIntegrationTest.class);
+		TemplateComponentUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootUUTResourceIntegrationTest.class);
+		TemplateRootUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogAttributeUUTResourceIntegrationTest.class);
+		CatalogAttributeUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentUUTResourceIntegrationTest.class);
+		CatalogComponentUUTResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateComponentTEQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateComponentTEQResourceIntegrationTest.java
new file mode 100644
index 0000000..abe991b
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateComponentTEQResourceIntegrationTest.java
@@ -0,0 +1,68 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateComponentResource for TestEquipment
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateComponentTEQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateComponentTEQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentTEQResourceIntegrationTest.prepareTestData();
+		CatalogComponentTEQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateRoot
+		TemplateRootTEQResourceIntegrationTest.prepareTestData();
+		TemplateRootTEQResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(TemplateComponentTEQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testequipment/" + getTestDataValue(TemplateRootTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testTplCompTEQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateComponent");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_CATALOGCOMPONENT_ID, new JsonPrimitive(getTestDataValue(CatalogComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateRootTEQResourceIntegrationTest.class);
+		TemplateRootTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+		CatalogComponentTEQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateComponentTSQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateComponentTSQResourceIntegrationTest.java
new file mode 100644
index 0000000..9546a38
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateComponentTSQResourceIntegrationTest.java
@@ -0,0 +1,68 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateComponentResource for TestSequence
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateComponentTSQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateComponentTSQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentTSQResourceIntegrationTest.prepareTestData();
+		CatalogComponentTSQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateRoot
+		TemplateRootTSQResourceIntegrationTest.prepareTestData();
+		TemplateRootTSQResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(TemplateComponentTSQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testsequence/" + getTestDataValue(TemplateRootTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testTplCompTSQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateComponent");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_CATALOGCOMPONENT_ID, new JsonPrimitive(getTestDataValue(CatalogComponentTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateRootTSQResourceIntegrationTest.class);
+		TemplateRootTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTSQResourceIntegrationTest.class);
+		CatalogComponentTEQResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateComponentUUTResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateComponentUUTResourceIntegrationTest.java
new file mode 100644
index 0000000..17d214c
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateComponentUUTResourceIntegrationTest.java
@@ -0,0 +1,68 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateComponentResource for UnitUnderTest
+ * {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateComponentUUTResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateComponentUUTResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogComponent
+		CatalogComponentUUTResourceIntegrationTest.prepareTestData();
+		CatalogComponentUUTResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateRoot
+		TemplateRootUUTResourceIntegrationTest.prepareTestData();
+		TemplateRootUUTResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(TemplateComponentUUTResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/unitundertest/" + getTestDataValue(TemplateRootUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testTplCompUUT");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateComponent");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_CATALOGCOMPONENT_ID, new JsonPrimitive(getTestDataValue(CatalogComponentUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateRootUUTResourceIntegrationTest.class);
+		TemplateRootUUTResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentUUTResourceIntegrationTest.class);
+		CatalogComponentUUTResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateRootTEQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateRootTEQResourceIntegrationTest.java
new file mode 100644
index 0000000..fd71d35
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateRootTEQResourceIntegrationTest.java
@@ -0,0 +1,55 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateRootResource for TestEquipment {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateRootTEQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateRootTEQResourceIntegrationTest");
+
+		// set up test data
+		setContextClass(TemplateRootTEQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testequipment");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testTplRootTEQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateRoot");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		JsonObject enumerationJson = new JsonObject();
+		enumerationJson.add("Enumeration", new JsonPrimitive("VersionState"));
+		enumerationJson.add("EnumerationValue", new JsonPrimitive("VALID"));
+		json.add("ValidFlag", enumerationJson);
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateRootTSQResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateRootTSQResourceIntegrationTest.java
new file mode 100644
index 0000000..44ea4ad
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateRootTSQResourceIntegrationTest.java
@@ -0,0 +1,55 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateRootResource for TestSequence {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateRootTSQResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateRootTSQResourceIntegrationTest");
+
+		// set up test data
+		setContextClass(TemplateRootTSQResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testsequence");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testTplRootTSQ");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateRoot");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		JsonObject enumerationJson = new JsonObject();
+		enumerationJson.add("Enumeration", new JsonPrimitive("VersionState"));
+		enumerationJson.add("EnumerationValue", new JsonPrimitive("VALID"));
+		json.add("ValidFlag", enumerationJson);
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateRootUUTResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateRootUUTResourceIntegrationTest.java
new file mode 100644
index 0000000..56ca583
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateRootUUTResourceIntegrationTest.java
@@ -0,0 +1,55 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateRootResource for UnitUnderTest {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateRootUUTResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateRootUUTResourceIntegrationTest");
+
+		// set up test data
+		setContextClass(TemplateRootUUTResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/unitundertest");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testTplRootpUUT");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateRoot");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		JsonObject enumerationJson = new JsonObject();
+		enumerationJson.add("Enumeration", new JsonPrimitive("VersionState"));
+		enumerationJson.add("EnumerationValue", new JsonPrimitive("VALID"));
+		json.add("ValidFlag", enumerationJson);
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateSensorAttributeResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateSensorAttributeResourceIntegrationTest.java
new file mode 100644
index 0000000..395b33b
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateSensorAttributeResourceIntegrationTest.java
@@ -0,0 +1,97 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateComponentResource for TestEquipment
+ * {@link ContextType}.
+ * 
+ * @author Philipp Schweinbenz, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateSensorAttributeResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateSensorAttributeResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogSensorAttribute
+		CatalogSensorAttributeResourceIntegrationTest.prepareTestData();
+		CatalogSensorAttributeResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateSensor
+		TemplateSensorResourceIntegrationTest.prepareTestData();
+		TemplateSensorResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(TemplateSensorAttributeResourceIntegrationTest.class);
+
+		// skip the creation test as TemplateSensorAttributes are implicitly created
+		// with the TemplateSensor
+		skipTest(TestType.CREATE);
+		// skip the deletion test as TemplateSensorAttributes can't be deleted
+		skipTest(TestType.DELETE);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testequipment/"
+				+ getTestDataValue(TemplateRootTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/"
+				+ getTestDataValue(TemplateComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID)
+				+ "/tplsensors/" + getTestDataValue(TemplateSensorResourceIntegrationTest.class, TESTDATA_ENTITY_ID)
+				+ "/tplsensorattrs");
+		putTestDataValue(TESTDATA_ENTITY_NAME,
+				getTestDataValue(CatalogSensorAttributeResourceIntegrationTest.class, TESTDATA_ENTITY_NAME));
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateAttribute");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(
+				getTestDataValue(CatalogSensorAttributeResourceIntegrationTest.class, TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		// get first TemplateSensorAttribute
+		TemplateSensorAttributeResourceIntegrationTest.findFirst();
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateSensorResourceIntegrationTest.class);
+		TemplateSensorResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateComponentTEQResourceIntegrationTest.class);
+		TemplateComponentTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootTEQResourceIntegrationTest.class);
+		TemplateRootTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogSensorAttributeResourceIntegrationTest.class);
+		CatalogSensorAttributeResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogSensorResourceIntegrationTest.class);
+		CatalogSensorResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+		CatalogComponentTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(QuantityResourceIntegrationTest.class);
+		QuantityResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateSensorResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateSensorResourceIntegrationTest.java
new file mode 100755
index 0000000..8e085c9
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateSensorResourceIntegrationTest.java
@@ -0,0 +1,87 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateSensorResource {@link ContextType}.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateSensorResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateAttributeTEQResourceIntegrationTest");
+
+		// prepare test data for creating the CatalogSensor
+		CatalogSensorResourceIntegrationTest.prepareTestData();
+		CatalogSensorResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TemplateComponent
+		TemplateComponentTEQResourceIntegrationTest.prepareTestData();
+		TemplateComponentTEQResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the Quantity
+		QuantityResourceIntegrationTest.prepareTestData();
+		QuantityResourceIntegrationTest.createEntity();
+
+		setContextClass(TemplateSensorResourceIntegrationTest.class);
+
+		// set up test data
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplroots/testequipment/" + getTestDataValue(TemplateRootTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplcomps/" + getTestDataValue(TemplateComponentTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/tplsensors");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testTplSensor");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateSensor");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_CATALOGSENSOR_ID, new JsonPrimitive(getTestDataValue(CatalogSensorResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_QUANTITY_ID, new JsonPrimitive(getTestDataValue(QuantityResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_QUANTITY_ID,
+				new JsonPrimitive(getTestDataValue(QuantityResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateComponentTEQResourceIntegrationTest.class);
+		TemplateComponentTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootTEQResourceIntegrationTest.class);
+		TemplateRootTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogSensorResourceIntegrationTest.class);
+		CatalogSensorResourceIntegrationTest.deleteEntity();
+
+		setContextClass(CatalogComponentTEQResourceIntegrationTest.class);
+		CatalogSensorResourceIntegrationTest.deleteEntity();
+
+		setContextClass(QuantityResourceIntegrationTest.class);
+		QuantityResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateTestResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateTestResourceIntegrationTest.java
new file mode 100755
index 0000000..46de394
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateTestResourceIntegrationTest.java
@@ -0,0 +1,54 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateTestResource.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateTestResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateTestResourceIntegrationTest");
+
+		// set up test data
+		setContextClass(TemplateTestResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tpltests");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testTplTest");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateTest");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		JsonObject enumerationJson = new JsonObject();
+		enumerationJson.add("Enumeration", new JsonPrimitive("VersionState"));
+		enumerationJson.add("EnumerationValue", new JsonPrimitive("VALID"));
+		json.add("ValidFlag", enumerationJson);
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateTestStepResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateTestStepResourceIntegrationTest.java
new file mode 100755
index 0000000..621a869
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateTestStepResourceIntegrationTest.java
@@ -0,0 +1,83 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateTestStepResource.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateTestStepResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateTestResourceIntegrationTest");
+
+		// prepare test data for creating the TemplateRoots
+		TemplateRootTEQResourceIntegrationTest.prepareTestData();
+		TemplateRootTEQResourceIntegrationTest.createEntity();
+		TemplateRootTSQResourceIntegrationTest.prepareTestData();
+		TemplateRootTSQResourceIntegrationTest.createEntity();
+		TemplateRootUUTResourceIntegrationTest.prepareTestData();
+		TemplateRootUUTResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(TemplateTestStepResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/tplteststeps");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testTplTestStep");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, ResourceConstants.ENTITYATTRIBUTE_TEMPLATETESTSTEP_ID);
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		JsonObject enumerationJson = new JsonObject();
+		enumerationJson.add("Enumeration", new JsonPrimitive("VersionState"));
+		enumerationJson.add("EnumerationValue", new JsonPrimitive("VALID"));
+		json.add("ValidFlag", enumerationJson);
+
+		json.add("TemplateTestEquipmentRoot",
+				new JsonPrimitive(getTestDataValue(TemplateRootTEQResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		json.add("TemplateTestSequenceRoot",
+				new JsonPrimitive(getTestDataValue(TemplateRootTSQResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		json.add("TemplateUnitUnderTestRoot",
+				new JsonPrimitive(getTestDataValue(TemplateRootUUTResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateRootTEQResourceIntegrationTest.class);
+		TemplateRootTEQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootTSQResourceIntegrationTest.class);
+		TemplateRootTSQResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateRootUUTResourceIntegrationTest.class);
+		TemplateRootUUTResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateTestStepUsageResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateTestStepUsageResourceIntegrationTest.java
new file mode 100644
index 0000000..b698607
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/TemplateTestStepUsageResourceIntegrationTest.java
@@ -0,0 +1,76 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for TemplateTestStepUsageResource.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class TemplateTestStepUsageResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing TemplateTestResourceIntegrationTest");
+
+		// prepare test data for creating the Test
+		TemplateTestResourceIntegrationTest.prepareTestData();
+		TemplateTestResourceIntegrationTest.createEntity();
+
+		// prepare test data for creating the TestStep
+		TemplateTestStepResourceIntegrationTest.prepareTestData();
+		TemplateTestStepResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(TemplateTestStepUsageResourceIntegrationTest.class);
+
+		// skip the update as TemplateTestStepUsages cannot be updated
+		// skipTest(TestType.UPDATE);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI,
+				"/tpltests/" + getTestDataValue(TemplateTestResourceIntegrationTest.class, TESTDATA_ENTITY_ID)
+						+ "/tplteststepusages");
+		// indicates that the value is set with the response data from create()
+		putTestDataValue(TESTDATA_ENTITY_NAME, TESTDATA_RANDOM_DATA);
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "TemplateTestStepUsage");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_TEMPLATETESTSTEP_ID, new JsonPrimitive(getTestDataValue(TemplateTestStepResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		json.add("DefaultActive", new JsonPrimitive(true));
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(TemplateTestResourceIntegrationTest.class);
+		TemplateTestResourceIntegrationTest.deleteEntity();
+
+		setContextClass(TemplateTestStepResourceIntegrationTest.class);
+		TemplateTestStepResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/UnitResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/UnitResourceIntegrationTest.java
new file mode 100644
index 0000000..9f1b1d6
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/UnitResourceIntegrationTest.java
@@ -0,0 +1,65 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for UnitResource.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class UnitResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing UnitResourceIntegrationTest");
+
+		// prepare test data for creating the PhysicalDimension
+		PhysicalDimensionResourceIntegrationTest.prepareTestData();
+		PhysicalDimensionResourceIntegrationTest.createEntity();
+
+		// set up test data
+		setContextClass(UnitResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/units");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testUnit");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "Unit");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		json.add(ResourceConstants.ENTITYATTRIBUTE_PHYSICALDIMENSION_ID, new JsonPrimitive(
+				getTestDataValue(PhysicalDimensionResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+
+		json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_PHYSICALDIMENSION_ID, new JsonPrimitive(
+				getTestDataValue(PhysicalDimensionResourceIntegrationTest.class, TESTDATA_ENTITY_ID)));
+		putTestDataValue(TESTDATA_UPDATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(PhysicalDimensionResourceIntegrationTest.class);
+		PhysicalDimensionResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/ValueListResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/ValueListResourceIntegrationTest.java
new file mode 100644
index 0000000..6b99ed5
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/ValueListResourceIntegrationTest.java
@@ -0,0 +1,47 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for ValueListResource.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class ValueListResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing ValueListResourceIntegrationTest");
+
+		// set up test data
+		setContextClass(ValueListResourceIntegrationTest.class);
+
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/valuelists");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testValueList");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "ValueList");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/ValueListValueResourceIntegrationTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/ValueListValueResourceIntegrationTest.java
new file mode 100644
index 0000000..ae67ad3
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/boundary/integrationtest/ValueListValueResourceIntegrationTest.java
@@ -0,0 +1,59 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.boundary.integrationtest;
+
+import org.eclipse.mdm.businessobjects.boundary.ResourceConstants;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+/**
+ * Test class for ValueListValueResource.
+ * 
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
+ * @see EntityResourceIntegrationTest
+ *
+ */
+public class ValueListValueResourceIntegrationTest extends EntityResourceIntegrationTest {
+
+	@BeforeClass
+	public static void prepareTestData() {
+		getLogger().debug("Preparing ValueListValueResourceIntegrationTest");
+
+		// prepare test data for creating the ValueList
+		ValueListResourceIntegrationTest.prepareTestData();
+		ValueListResourceIntegrationTest.createEntity();
+
+		// reset the context
+		setContextClass(ValueListValueResourceIntegrationTest.class);
+
+		// set up test data
+		putTestDataValue(TESTDATA_RESOURCE_URI, "/valuelists/" + getTestDataValue(ValueListResourceIntegrationTest.class, TESTDATA_ENTITY_ID) + "/values");
+		putTestDataValue(TESTDATA_ENTITY_NAME, "testValueListValue");
+		putTestDataValue(TESTDATA_ENTITY_TYPE, "ValueListValue");
+
+		JsonObject json = new JsonObject();
+		json.add(ResourceConstants.ENTITYATTRIBUTE_NAME, new JsonPrimitive(getTestDataValue(TESTDATA_ENTITY_NAME)));
+		putTestDataValue(TESTDATA_CREATE_JSON_BODY, json.toString());
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() {
+		setContextClass(ValueListResourceIntegrationTest.class);
+		ValueListResourceIntegrationTest.deleteEntity();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/context/ContextActivityMockHelper.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/context/ContextActivityMockHelper.java
new file mode 100644
index 0000000..1cf3b74
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/context/ContextActivityMockHelper.java
@@ -0,0 +1,178 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control.context;
+
+import org.eclipse.mdm.api.base.adapter.ChildrenStore;
+import org.eclipse.mdm.api.base.adapter.Core;
+import org.eclipse.mdm.api.base.model.*;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Constructor;
+import java.util.*;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.when;
+
+public final class ContextActivityMockHelper {
+
+	public static final int ITEM_COUNT = 1;
+	public static final int CC_COUNT = 10;
+	public static final int CC_VALUE_COUNT = 10;
+	
+	private ContextActivityMockHelper() {
+	}
+
+	public static ConnectorService createConnectorMock() throws Exception {
+
+		ConnectorService connectorBean = Mockito.mock(ConnectorService.class);
+
+		List<ApplicationContext> contextList = new ArrayList<>();
+		for (int i = 1; i <= ITEM_COUNT; i++) {
+			contextList.add(createContextMock("MDMENV_" + i));
+		}
+		when(connectorBean.getContexts()).thenReturn(contextList);
+		when(connectorBean.getContextByName(anyString())).thenReturn(contextList.get(0));
+		return connectorBean;
+	}
+
+	private static ApplicationContext createContextMock(String sourceName) throws Exception {
+
+		Environment env = createEntityMock(Environment.class, sourceName, sourceName, "1", null);
+
+		EntityManager em = Mockito.mock(EntityManager.class);
+
+		TestStep testStep = createEntityMock(TestStep.class, "TestStepWithContext", "MDM", "1", null);
+		Measurement measurement = createEntityMock(Measurement.class, "MeasurementWithContext", "MDM", "1", null);
+
+		when(em.loadEnvironment()).thenReturn(env);
+		when(em.load(TestStep.class, "1")).thenReturn(testStep);
+		when(em.load(Measurement.class, "1")).thenReturn(measurement);
+		when(em.loadParent(measurement, Measurement.PARENT_TYPE_TESTSTEP)).thenReturn(Optional.of(testStep));
+
+		List<Measurement> mList = new ArrayList<>();
+		mList.add(measurement);
+
+		when(em.loadChildren(testStep, TestStep.CHILD_TYPE_MEASUREMENT)).thenReturn(mList);
+
+		Map<ContextType, ContextRoot> orderedContext = createContext("ordered");
+		Map<ContextType, ContextRoot> measuredContext = createContext("measured");
+		when(em.loadContexts(testStep)).thenReturn(orderedContext);
+		when(em.loadContexts(measurement)).thenReturn(measuredContext);
+
+		ApplicationContext context = Mockito.mock(ApplicationContext.class);
+		when(context.getEntityManager()).thenReturn(Optional.of(em));
+		return context;
+	}
+
+	private static <T extends Entity> T createEntityMock(Class<T> type, String name, String sourceName, String id,
+			ChildrenStore childrenStore) throws Exception {
+
+		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);
+		if (childrenStore != null) {
+			when(core.getChildrenStore()).thenReturn(childrenStore);
+		}
+
+		if (ContextRoot.class.equals(type)) {
+			if (name.contains("TSQ_")) {
+				when(core.getTypeName()).thenReturn("TestSequence");
+			} else if (name.contains("UUT")) {
+				when(core.getTypeName()).thenReturn("UnitUnderTest");
+			} else {
+				when(core.getTypeName()).thenReturn("TestEquipment");
+			}
+		}
+
+		Constructor<T> constructor = type.getDeclaredConstructor(Core.class);
+		constructor.setAccessible(true);
+		T instance = constructor.newInstance(core);
+		constructor.setAccessible(false);
+		return instance;
+	}
+
+	private static ContextRoot createUUTContextRootMock(String type) throws Exception {
+
+		List<ContextComponent> ccList = createContextComponentMocks("UUT");
+
+		ChildrenStore childrenStore = new ChildrenStore();
+		for (ContextComponent cc : ccList) {
+			childrenStore.add(cc);
+		}
+
+		return createEntityMock(ContextRoot.class, "MessungUUT_" + type, "MDM", "1", childrenStore);
+	}
+
+	private static ContextRoot createTSQContextRootMock(String type) throws Exception {
+
+		List<ContextComponent> ccList = createContextComponentMocks("TSQ");
+
+		ChildrenStore childrenStore = new ChildrenStore();
+		for (ContextComponent cc : ccList) {
+			childrenStore.add(cc);
+		}
+
+		return createEntityMock(ContextRoot.class, "MessungTSQ_" + type, "MDM", "1", childrenStore);
+	}
+
+	private static ContextRoot createTEQContextRootMock(String type) throws Exception {
+
+		List<ContextComponent> ccList = createContextComponentMocks("TEQ");
+
+		ChildrenStore childrenStore = new ChildrenStore();
+		for (ContextComponent cc : ccList) {
+			childrenStore.add(cc);
+		}
+
+		return createEntityMock(ContextRoot.class, "MessungTEQ_" + type, "MDM", "1", childrenStore);
+	}
+
+	private static List<ContextComponent> createContextComponentMocks(String type) throws Exception {
+		List<ContextComponent> ccList = new ArrayList<>();
+		for (int i = 1; i <= CC_COUNT; i++) {
+			ContextComponent cc = createEntityMock(ContextComponent.class, type + "_ContextComponent_" + i, "MDM",
+					Integer.toString(i), null);
+			when(cc.getValues()).thenReturn(createValues(type));
+			ccList.add(cc);
+		}
+		return ccList;
+	}
+
+	private static Map<String, Value> createValues(String type) {
+		Map<String, Value> valueMap = new HashMap<>();
+		for (int i = 1; i <= CC_VALUE_COUNT; i++) {
+			String vName = type + "_ValueName_" + i;
+			valueMap.put(vName, ValueType.STRING.create(vName, type + "_Value_" + i));
+		}
+		return valueMap;
+	}
+
+	private static Map<ContextType, ContextRoot> createContext(String type) throws Exception {
+		Map<ContextType, ContextRoot> contextMap = new HashMap<>();
+		contextMap.put(ContextType.UNITUNDERTEST, createUUTContextRootMock(type));
+		contextMap.put(ContextType.TESTSEQUENCE, createTSQContextRootMock(type));
+		contextMap.put(ContextType.TESTEQUIPMENT, createTEQContextRootMock(type));
+		return contextMap;
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/context/ContextActivityTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/context/ContextActivityTest.java
new file mode 100644
index 0000000..b4ec497
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/context/ContextActivityTest.java
@@ -0,0 +1,96 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control.context;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eclipse.mdm.api.base.model.ContextComponent;
+import org.eclipse.mdm.api.base.model.ContextRoot;
+import org.eclipse.mdm.api.base.model.ContextType;
+import org.eclipse.mdm.api.base.model.Value;
+import org.eclipse.mdm.businessobjects.control.ContextActivity;
+import org.junit.Test;
+
+public class ContextActivityTest {
+
+	@Test
+	public void testGetTestStepContext() throws Exception {
+
+		ContextActivity contextActitity = createMockedActivity();
+		Map<String, Map<ContextType, ContextRoot>> contextMap = contextActitity.getTestStepContext("MDM", "1");
+
+		assertNotNull("context should not be null", contextMap);
+		checkContextContent(contextMap);
+
+	}
+
+	@Test
+	public void testGetMeasurementContext() throws Exception {
+
+		ContextActivity contextActitity = createMockedActivity();
+		Map<String, Map<ContextType, ContextRoot>> contextMap = contextActitity.getMeasurementContext("MDM", "1");
+
+		assertNotNull("context should not be null", contextMap);
+		checkContextContent(contextMap);
+	}
+
+	private void checkContextContent(Map<String, Map<ContextType, ContextRoot>> contextMap) {
+
+		Map<ContextType, ContextRoot> orderedContext = contextMap.get(ContextActivity.CONTEXT_GROUP_ORDERED);
+		Set<Entry<ContextType, ContextRoot>> orderedEntrySet = orderedContext.entrySet();
+		assertEquals("size of entry set should be 3", 3, orderedEntrySet.size());
+
+		for (Entry<ContextType, ContextRoot> entry : orderedEntrySet) {
+			ContextRoot cr = entry.getValue();
+			List<ContextComponent> ccList = cr.getContextComponents();
+			assertEquals("size of context components should be 10", 10, ccList.size());
+			for (ContextComponent cc : ccList) {
+				Map<String, Value> values = cc.getValues();
+				assertEquals("size of value map should be 10", 10, values.size());
+			}
+		}
+
+		Map<ContextType, ContextRoot> measuredContext = contextMap.get(ContextActivity.CONTEXT_GROUP_MEASURED);
+		Set<Entry<ContextType, ContextRoot>> measuredEntrySet = measuredContext.entrySet();
+		assertEquals("size of entry set should be 3", 3, measuredEntrySet.size());
+
+		for (Entry<ContextType, ContextRoot> entry : measuredEntrySet) {
+			ContextRoot cr = entry.getValue();
+			List<ContextComponent> ccList = cr.getContextComponents();
+			assertEquals("size of context components should be 10", 10, ccList.size());
+			for (ContextComponent cc : ccList) {
+				Map<String, Value> values = cc.getValues();
+				assertEquals("size of value map should be 10", 10, values.size());
+			}
+		}
+	}
+
+	public ContextActivity createMockedActivity() throws Exception {
+		ContextActivity contextActivity = new ContextActivity();
+		Field field = contextActivity.getClass().getDeclaredField("connectorService");
+		field.setAccessible(true);
+		field.set(contextActivity, ContextActivityMockHelper.createConnectorMock());
+		field.setAccessible(false);
+		return contextActivity;
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/i18n/I18NActivityMockHelper.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/i18n/I18NActivityMockHelper.java
new file mode 100644
index 0000000..f6e8833
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/i18n/I18NActivityMockHelper.java
@@ -0,0 +1,162 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control.i18n;
+
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.Core;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.adapter.ModelManager;
+import org.eclipse.mdm.api.base.model.Channel;
+import org.eclipse.mdm.api.base.model.ChannelGroup;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Measurement;
+import org.eclipse.mdm.api.base.model.Test;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.model.Value;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.mockito.Mockito;
+
+public final class I18NActivityMockHelper {
+
+	public static final int ITEM_COUNT = 3;
+	public static final int ATTRIBUTE_COUNT = 2;
+	
+	private I18NActivityMockHelper() {
+	}
+
+	public static ConnectorService createConnectorMock() throws Exception {
+
+		ConnectorService connectorBean = Mockito.mock(ConnectorService.class);
+
+		List<ApplicationContext> emList = new ArrayList<>();
+		for (int i = 0; i < ITEM_COUNT; i++) {
+			emList.add(createContextMock("MDMENV_" + i));
+		}
+		when(connectorBean.getContexts()).thenReturn(emList);
+		when(connectorBean.getContextByName(anyString())).thenReturn(emList.get(0));
+		return connectorBean;
+	}
+
+	private static ApplicationContext createContextMock(String sourceName) throws Exception {
+
+		Environment env = createEntityMock(Environment.class, sourceName, sourceName, "1");
+
+		EntityManager em = Mockito.mock(EntityManager.class);
+
+		when(em.loadEnvironment()).thenReturn(env);
+
+		ModelManager modelManagerMock = createModelManagerMock();
+		
+		ApplicationContext context = Mockito.mock(ApplicationContext.class);
+		when(context.getEntityManager()).thenReturn(Optional.of(em));
+		when(context.getModelManager()).thenReturn(Optional.of(modelManagerMock));
+		return context;
+	}
+
+	private static ModelManager createModelManagerMock() throws Exception {
+
+		List<EntityType> etList = new ArrayList<>();
+
+		EntityType envET = Mockito.mock(EntityType.class);
+		List<Attribute> envAttrList = createAttributeList(Environment.class, ATTRIBUTE_COUNT);
+		when(envET.getName()).thenReturn("Environment");
+		when(envET.getAttributes()).thenReturn(envAttrList);
+		etList.add(envET);
+
+		EntityType testET = Mockito.mock(EntityType.class);
+		List<Attribute> testAttrList = createAttributeList(Test.class, ATTRIBUTE_COUNT);
+		when(testET.getName()).thenReturn("Test");
+		when(testET.getAttributes()).thenReturn(testAttrList);
+		etList.add(testET);
+
+		EntityType testStepET = Mockito.mock(EntityType.class);
+		List<Attribute> testStepAttrList = createAttributeList(TestStep.class, ATTRIBUTE_COUNT);
+		when(testStepET.getName()).thenReturn("TestStep");
+		when(testStepET.getAttributes()).thenReturn(testStepAttrList);
+		etList.add(testStepET);
+
+		EntityType measurementET = Mockito.mock(EntityType.class);
+		List<Attribute> measurementAttrList = createAttributeList(Measurement.class, ATTRIBUTE_COUNT);
+		when(measurementET.getName()).thenReturn("Measurement");
+		when(measurementET.getAttributes()).thenReturn(measurementAttrList);
+		etList.add(measurementET);
+
+		EntityType channelGroupET = Mockito.mock(EntityType.class);
+		List<Attribute> channelGroupAttrList = createAttributeList(ChannelGroup.class, ATTRIBUTE_COUNT);
+		when(channelGroupET.getName()).thenReturn("ChannelGroup");
+		when(channelGroupET.getAttributes()).thenReturn(channelGroupAttrList);
+		etList.add(channelGroupET);
+
+		EntityType channelET = Mockito.mock(EntityType.class);
+		List<Attribute> channelAttrList = createAttributeList(Channel.class, ATTRIBUTE_COUNT);
+		when(channelET.getName()).thenReturn("Channel");
+		when(channelET.getAttributes()).thenReturn(channelAttrList);
+		etList.add(channelET);
+
+		ModelManager modelManager = Mockito.mock(ModelManager.class);
+		when(modelManager.getEntityType(Environment.class)).thenReturn(envET);
+		when(modelManager.getEntityType(Test.class)).thenReturn(testET);
+		when(modelManager.getEntityType(TestStep.class)).thenReturn(testStepET);
+		when(modelManager.getEntityType(Measurement.class)).thenReturn(measurementET);
+		when(modelManager.getEntityType(ChannelGroup.class)).thenReturn(channelGroupET);
+		when(modelManager.getEntityType(Channel.class)).thenReturn(channelET);
+		when(modelManager.listEntityTypes()).thenReturn(etList);
+		return modelManager;
+	}
+
+	private static List<Attribute> createAttributeList(Class<? extends Entity> type, long count) {
+		List<Attribute> attributeList = new ArrayList<>();
+		for (int i = 0; i < count; i++) {
+			Attribute attribute = Mockito.mock(Attribute.class);
+			when(attribute.getName()).thenReturn(type.getSimpleName() + "_attribute_" + i);
+			attributeList.add(attribute);
+		}
+		return attributeList;
+	}
+
+	private static <T extends Entity> T createEntityMock(Class<T> type, String name, String sourceName, String id)
+			throws Exception {
+
+		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<T> constructor = type.getDeclaredConstructor(Core.class);
+		constructor.setAccessible(true);
+		T instance = constructor.newInstance(core);
+		constructor.setAccessible(false);
+		return instance;
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/i18n/I18NActivityTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/i18n/I18NActivityTest.java
new file mode 100644
index 0000000..9100dc0
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/i18n/I18NActivityTest.java
@@ -0,0 +1,132 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control.i18n;
+
+import static org.junit.Assert.assertEquals;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Channel;
+import org.eclipse.mdm.api.base.model.ChannelGroup;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Measurement;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.businessobjects.control.I18NActivity;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.junit.Test;
+
+public class I18NActivityTest {
+
+	@Test
+	public void testLocalizeAttributes() throws Exception {
+
+		I18NActivity i18nActivity = createdMockedActivity();
+
+		Map<Attribute, String> locEnvironmentAttrs = i18nActivity.localizeAttributes("default", Environment.class);
+		Map<Attribute, String> locTestAttrs = i18nActivity.localizeAttributes("default",
+				org.eclipse.mdm.api.base.model.Test.class);
+		Map<Attribute, String> locTestStepAttrs = i18nActivity.localizeAttributes("default", TestStep.class);
+		Map<Attribute, String> locMeasurementAttrs = i18nActivity.localizeAttributes("default", Measurement.class);
+		Map<Attribute, String> locChannelGroupAttrs = i18nActivity.localizeAttributes("default", ChannelGroup.class);
+		Map<Attribute, String> locChannelAttrs = i18nActivity.localizeAttributes("default", Channel.class);
+
+		int expected = 2;
+		assertEquals("map of environment attributes should contain '" + expected + "' localized attributes", expected,
+				locEnvironmentAttrs.size());
+
+		assertEquals("map of test attributes should contain '" + expected + "' localized attributes", expected,
+				locTestAttrs.size());
+
+		assertEquals("map of test step attributes should contain '" + expected + "' localized attributes", expected,
+				locTestStepAttrs.size());
+
+		assertEquals("map of measurement attributes should contain '" + expected + "' localized attributes", expected,
+				locMeasurementAttrs.size());
+
+		assertEquals("map of channel group attributes should contain '" + expected + "' localized attributes", expected,
+				locChannelGroupAttrs.size());
+
+		assertEquals("map of channel attributes should contain '" + expected + "' localized attributes", expected,
+				locChannelAttrs.size());
+
+	}
+
+	@Test
+	public void testLocalizeType() throws Exception {
+		I18NActivity i18nActivity = createdMockedActivity();
+
+		Map<EntityType, String> locEnvironmentType = i18nActivity.localizeType("default", Environment.class);
+		Map<EntityType, String> locTestType = i18nActivity.localizeType("default",
+				org.eclipse.mdm.api.base.model.Test.class);
+		Map<EntityType, String> locTestStepType = i18nActivity.localizeType("default", TestStep.class);
+		Map<EntityType, String> locMeasurementType = i18nActivity.localizeType("default", Measurement.class);
+		Map<EntityType, String> locChannelGroupType = i18nActivity.localizeType("default", ChannelGroup.class);
+		Map<EntityType, String> locChannelType = i18nActivity.localizeType("default", Channel.class);
+
+		int expected = 1;
+		assertEquals("map of Environment types should contain '" + expected + "' localized type", expected,
+				locEnvironmentType.size());
+		assertEquals("map of Test types should contain '" + expected + "' localized type", expected,
+				locTestType.size());
+		assertEquals("map of TestStep types should contain '" + expected + "' localized type", expected,
+				locTestStepType.size());
+		assertEquals("map of Measurement types should contain '" + expected + "' localized type", expected,
+				locMeasurementType.size());
+		assertEquals("map of ChannelGroup types should contain '" + expected + "' localized type", expected,
+				locChannelGroupType.size());
+		assertEquals("map of Channel types should contain '" + expected + "' localized type", expected,
+				locChannelType.size());
+	}
+
+	@Test
+	public void testAllLocalizeAttributes() throws Exception {
+		I18NActivity i18nActivity = createdMockedActivity();
+
+		Map<Attribute, String> localizedMap = i18nActivity.localizeAllAttributes("default");
+
+		int expected = 12; // 6 types with 2 attributes
+
+		assertEquals("map of all attributes should contain '" + expected + "' localized attributes", expected,
+				localizedMap.size());
+	}
+
+	@Test
+	public void testLocalizeAllTypes() throws Exception {
+		I18NActivity i18nActivity = createdMockedActivity();
+
+		Map<EntityType, String> localizedMap = i18nActivity.localizeAllTypes("default");
+
+		int expected = 6;
+		assertEquals("map of all types should contain '" + expected + "' localized types", expected,
+				localizedMap.size());
+	}
+
+	private I18NActivity createdMockedActivity() throws Exception {
+
+		ConnectorService connectorBeanMock = I18NActivityMockHelper.createConnectorMock();
+
+		I18NActivity i18nActivity = new I18NActivity();
+		Field fieldConnector = i18nActivity.getClass().getDeclaredField("connectorService");
+		fieldConnector.setAccessible(true);
+		fieldConnector.set(i18nActivity, connectorBeanMock);
+		fieldConnector.setAccessible(false);
+
+		return i18nActivity;
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/navigation/NavigationActivityMockHelper.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/navigation/NavigationActivityMockHelper.java
new file mode 100644
index 0000000..fd5aa6a
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/navigation/NavigationActivityMockHelper.java
@@ -0,0 +1,198 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control.navigation;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.mdm.api.base.adapter.Core;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.adapter.ModelManager;
+import org.eclipse.mdm.api.base.model.Channel;
+import org.eclipse.mdm.api.base.model.ChannelGroup;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Measurement;
+import org.eclipse.mdm.api.base.model.Test;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.model.Value;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.businessobjects.boundary.EnvironmentService;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+
+import com.google.common.collect.ImmutableList;
+
+public final class NavigationActivityMockHelper {
+
+	public static final int ITEM_COUNT = 5;
+	
+	private NavigationActivityMockHelper() {
+	}
+
+	public static ConnectorService createConnectorMock() throws Exception {
+
+		ConnectorService connectorBean = mock(ConnectorService.class);
+
+		List<ApplicationContext> contextList = new ArrayList<>();
+		for (int i = 1; i <= ITEM_COUNT; i++) {
+			contextList.add(createContextMock("MDMENV_" + i));
+		}
+		when(connectorBean.getContexts()).thenReturn(contextList);
+		when(connectorBean.getContextByName(anyString())).thenReturn(contextList.get(0));
+		return connectorBean;
+	}
+
+	private static ApplicationContext createContextMock(String sourceName) throws Exception {
+
+		Environment env = createEntityMock(Environment.class, sourceName, sourceName, "1");
+
+		EntityManager em = mock(EntityManager.class);
+
+		when(em.loadEnvironment()).thenReturn(env);
+
+		List<Test> testMocks = createTestMocks(ITEM_COUNT, sourceName);
+		when(em.loadAll(Test.class)).thenReturn(testMocks);
+
+		List<TestStep> testStepMocks = createTestStepMocks(ITEM_COUNT, sourceName);
+		when(em.loadChildren(any(), eq(TestStep.class))).thenReturn(testStepMocks);
+
+		List<Measurement> measurementMocks = createMeasurementMocks(ITEM_COUNT, sourceName);
+		when(em.loadChildren(any(), eq(Measurement.class))).thenReturn(measurementMocks);
+
+		List<ChannelGroup> channelGroupMocks = createChannelGroupMocks(ITEM_COUNT, sourceName);
+		when(em.loadChildren(any(), eq(ChannelGroup.class))).thenReturn(channelGroupMocks);
+
+		List<Channel> channelMocks = createChannelMocks(ITEM_COUNT, sourceName);
+		when(em.loadChildren(any(), eq(Channel.class))).thenReturn(channelMocks);
+
+		ModelManager modelManagerMock = createModelManagerMock();
+
+		ApplicationContext context = mock(ApplicationContext.class);
+		when(context.getEntityManager()).thenReturn(Optional.of(em));
+		when(context.getModelManager()).thenReturn(Optional.of(modelManagerMock));
+		return context;
+	}
+
+	private static List<Test> createTestMocks(int count, String sourceName) throws Exception {
+		List<Test> list = new ArrayList<>();
+		for (int i = 1; i <= count; i++) {
+			list.add(createEntityMock(Test.class, "Test_" + count, sourceName, Integer.toString(count)));
+		}
+		return list;
+	}
+
+	private static List<TestStep> createTestStepMocks(int count, String sourceName) throws Exception {
+		List<TestStep> list = new ArrayList<>();
+		for (int i = 1; i <= count; i++) {
+			list.add(createEntityMock(TestStep.class, "TestStep_" + count, sourceName, Integer.toString(count)));
+		}
+		return list;
+	}
+
+	private static List<Measurement> createMeasurementMocks(int count, String sourceName) throws Exception {
+		List<Measurement> list = new ArrayList<>();
+		for (int i = 1; i <= count; i++) {
+			list.add(createEntityMock(Measurement.class, "Measurement_" + count, sourceName, Integer.toString(count)));
+		}
+		return list;
+	}
+
+	private static List<ChannelGroup> createChannelGroupMocks(int count, String sourceName) throws Exception {
+		List<ChannelGroup> list = new ArrayList<>();
+		for (int i = 1; i <= count; i++) {
+			list.add(createEntityMock(ChannelGroup.class, "ChannelGroup_" + count, sourceName, Integer.toString(count)));
+		}
+		return list;
+	}
+
+	private static List<Channel> createChannelMocks(int count, String sourceName) throws Exception {
+		List<Channel> list = new ArrayList<>();
+		for (int i = 1; i <= count; i++) {
+			list.add(createEntityMock(Channel.class, "Channel_" + count, sourceName, Integer.toString(count)));
+		}
+		return list;
+	}
+
+	private static ModelManager createModelManagerMock() throws Exception {
+
+		EntityType envET = mock(EntityType.class);
+		when(envET.getName()).thenReturn("Environment");
+
+		EntityType testET = mock(EntityType.class);
+		when(testET.getName()).thenReturn("Test");
+
+		EntityType testStepET = mock(EntityType.class);
+		when(testStepET.getName()).thenReturn("TestStep");
+
+		EntityType measurementET = mock(EntityType.class);
+		when(measurementET.getName()).thenReturn("Measurement");
+
+		EntityType channelGroupET = mock(EntityType.class);
+		when(channelGroupET.getName()).thenReturn("ChannelGroup");
+
+		EntityType channelET = mock(EntityType.class);
+		when(channelET.getName()).thenReturn("Channel");
+
+		ModelManager modelManager = mock(ModelManager.class);
+		when(modelManager.getEntityType(Environment.class)).thenReturn(envET);
+		when(modelManager.getEntityType(Test.class)).thenReturn(testET);
+		when(modelManager.getEntityType(TestStep.class)).thenReturn(testStepET);
+		when(modelManager.getEntityType(Measurement.class)).thenReturn(measurementET);
+		when(modelManager.getEntityType(ChannelGroup.class)).thenReturn(channelGroupET);
+		when(modelManager.getEntityType(Channel.class)).thenReturn(channelET);
+
+		return modelManager;
+	}
+
+	private static <T extends Entity> T createEntityMock(Class<T> type, String name, String sourceName, String id)
+			throws Exception {
+
+		HashMap<String, Value> map = new HashMap<String, Value>();
+		map.put("Name", ValueType.STRING.create("Name", name));
+
+		Core core = mock(Core.class);
+		when(core.getSourceName()).thenReturn(sourceName);
+		when(core.getValues()).thenReturn(map);
+		when(core.getID()).thenReturn(id);
+
+		Constructor<T> constructor = type.getDeclaredConstructor(Core.class);
+		constructor.setAccessible(true);
+		T instance = constructor.newInstance(core);
+		constructor.setAccessible(false);
+		return instance;
+	}
+
+	public static EnvironmentService createEnvironmentMock() {
+		EnvironmentService environmentService = mock(EnvironmentService.class);
+		Environment env = mock(Environment.class);
+
+		when(env.getSourceName()).thenReturn("MDMENV_1");
+		when(environmentService.getEnvironments()).thenReturn(ImmutableList.of(env));
+
+		return environmentService;
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/navigation/NavigatorActivityTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/navigation/NavigatorActivityTest.java
new file mode 100644
index 0000000..6948570
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/navigation/NavigatorActivityTest.java
@@ -0,0 +1,120 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control.navigation;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import org.eclipse.mdm.api.base.model.Channel;
+import org.eclipse.mdm.api.base.model.ChannelGroup;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Measurement;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.businessobjects.control.NavigationActivity;
+import org.junit.Before;
+import org.junit.Test;
+
+public class NavigatorActivityTest {
+
+	private NavigationActivity navigationActivity;
+	private String sourceName = "MDMENV_1";
+
+	@Before
+	public void setup() throws Exception {
+		navigationActivity = new NavigationActivity(NavigationActivityMockHelper.createConnectorMock());
+	}
+
+	@Test
+	public void testGetEnvironments() throws Exception {
+
+		List<Environment> envList = navigationActivity.getEnvironments();
+
+		assertNotNull("environment list should be not null", envList);
+		assertThat(envList.size(), is(5));
+	}
+
+	@Test
+	public void testGetTests() throws Exception {
+		List<org.eclipse.mdm.api.base.model.Test> testList = navigationActivity.getTests(sourceName);
+
+		assertNotNull("test list should be not null", testList);
+		assertEquals("test list size should be " + NavigationActivityMockHelper.ITEM_COUNT,
+				NavigationActivityMockHelper.ITEM_COUNT, testList.size());
+	}
+
+	@Test
+	public void testGetTestSteps() throws Exception {
+		List<org.eclipse.mdm.api.base.model.Test> testList = navigationActivity.getTests(sourceName);
+		org.eclipse.mdm.api.base.model.Test test = testList.get(0);
+		List<TestStep> testStepList = navigationActivity.getTestSteps(sourceName, test.getID());
+
+		assertNotNull("teststep list should be not null", testStepList);
+		assertEquals("teststep list size should be " + NavigationActivityMockHelper.ITEM_COUNT,
+				NavigationActivityMockHelper.ITEM_COUNT, testStepList.size());
+	}
+
+	@Test
+	public void testGetMeasurements() throws Exception {
+		List<org.eclipse.mdm.api.base.model.Test> testList = navigationActivity.getTests(sourceName);
+		org.eclipse.mdm.api.base.model.Test test = testList.get(0);
+		List<TestStep> testStepList = navigationActivity.getTestSteps(sourceName, test.getID());
+		TestStep testStep = testStepList.get(0);
+		List<Measurement> measurementList = navigationActivity.getMeasurements(sourceName, testStep.getID());
+
+		assertNotNull("measurement list should be not null", measurementList);
+		assertEquals("measurement list size should be " + NavigationActivityMockHelper.ITEM_COUNT,
+				NavigationActivityMockHelper.ITEM_COUNT, measurementList.size());
+
+	}
+
+	@Test
+	public void testGetChannelGroups() throws Exception {
+		List<org.eclipse.mdm.api.base.model.Test> testList = navigationActivity.getTests(sourceName);
+		org.eclipse.mdm.api.base.model.Test test = testList.get(0);
+		List<TestStep> testStepList = navigationActivity.getTestSteps(sourceName, test.getID());
+		TestStep testStep = testStepList.get(0);
+		List<Measurement> measurementList = navigationActivity.getMeasurements(sourceName, testStep.getID());
+		Measurement measurement = measurementList.get(0);
+		List<ChannelGroup> channelGroupList = navigationActivity.getChannelGroups(sourceName, measurement.getID());
+
+		assertNotNull("channel group list should be not null", channelGroupList);
+		assertEquals("channel group list size should be " + NavigationActivityMockHelper.ITEM_COUNT,
+				NavigationActivityMockHelper.ITEM_COUNT, channelGroupList.size());
+	}
+
+	@Test
+	public void testGetChannels() throws Exception {
+		List<org.eclipse.mdm.api.base.model.Test> testList = navigationActivity.getTests(sourceName);
+		org.eclipse.mdm.api.base.model.Test test = testList.get(0);
+		List<TestStep> testStepList = navigationActivity.getTestSteps(sourceName, test.getID());
+		TestStep testStep = testStepList.get(0);
+		List<Measurement> measurementList = navigationActivity.getMeasurements(sourceName, testStep.getID());
+		Measurement measurement = measurementList.get(0);
+		List<ChannelGroup> channelGroupList = navigationActivity.getChannelGroups(sourceName, measurement.getID());
+		ChannelGroup channelGroup = channelGroupList.get(0);
+		List<Channel> channelList = navigationActivity.getChannels(sourceName, channelGroup.getID());
+
+		assertNotNull("channel list should be not null", channelList);
+		assertEquals("channel list size should be " + NavigationActivityMockHelper.ITEM_COUNT,
+				NavigationActivityMockHelper.ITEM_COUNT, channelList.size());
+
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/search/FilterParserTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/search/FilterParserTest.java
new file mode 100644
index 0000000..57c411c
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/search/FilterParserTest.java
@@ -0,0 +1,678 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.businessobjects.control.search;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.eclipse.mdm.api.base.query.ComparisonOperator.*;
+import static org.eclipse.mdm.api.base.query.ComparisonOperator.GREATER_THAN;
+import static org.eclipse.mdm.api.base.query.ComparisonOperator.GREATER_THAN_OR_EQUAL;
+import static org.eclipse.mdm.api.base.query.ComparisonOperator.IN_SET;
+import static org.eclipse.mdm.api.base.query.ComparisonOperator.LESS_THAN;
+import static org.eclipse.mdm.api.base.query.ComparisonOperator.LESS_THAN_OR_EQUAL;
+import static org.eclipse.mdm.api.base.query.ComparisonOperator.LIKE;
+import static org.eclipse.mdm.api.base.query.ComparisonOperator.NOT_EQUAL;
+import static org.eclipse.mdm.businessobjects.control.FilterParser.parseFilterString;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.assertj.core.api.SoftAssertions;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.query.ComparisonOperator;
+import org.eclipse.mdm.api.base.query.Filter;
+import org.junit.Test;
+
+public class FilterParserTest {
+	private List<EntityType> entities = SearchMockHelper.createETListMock();
+	private Map<String, EntityType> map = entities.stream().collect(Collectors.toMap(EntityType::getName, e -> e));
+
+	@Test
+	public void testEmpty() throws Exception {
+		assertThat(parseFilterString(entities, "").isEmtpty()).isTrue();
+	}
+	
+	@Test
+	public void testNull() throws Exception {
+		assertThat(parseFilterString(entities, null).isEmtpty()).isTrue();
+	}
+	
+
+	@Test
+	public void testOperatorEqual() throws Exception {
+
+		String filterString = "TestStep.Name eq 'test'";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	@Test
+	public void testOperatorNotEqual() throws Exception {
+
+		String filterString = "TestStep.Name ne 'test'";
+		
+		Filter expected = Filter.and().add(NOT_EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorLessThan() throws Exception {
+
+		String filterString = "TestStep.LongAttribute lt 10";
+		
+		Filter expected = Filter.and().add(LESS_THAN.create(map.get("TestStep").getAttribute("LongAttribute"), 10L));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorLessThanOrEqual() throws Exception {
+
+		String filterString = "TestStep.LongAttribute le 10";
+		
+		Filter expected = Filter.and().add(LESS_THAN_OR_EQUAL.create(map.get("TestStep").getAttribute("LongAttribute"), 10L));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorGreaterThan() throws Exception {
+
+		String filterString = "TestStep.LongAttribute gt 10";
+		
+		Filter expected = Filter.and().add(GREATER_THAN.create(map.get("TestStep").getAttribute("LongAttribute"), 10L));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorGreaterThanOrEqual() throws Exception {
+
+		String filterString = "TestStep.LongAttribute ge 10";
+		
+		Filter expected = Filter.and().add(GREATER_THAN_OR_EQUAL.create(map.get("TestStep").getAttribute("LongAttribute"), 10L));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorNotInSet() throws Exception {
+
+		String filterString = "TestStep.Name not_in ('test', 'measurement', 'crash')";
+		
+		Filter expected = Filter.and().add(NOT_IN_SET.create(map.get("TestStep").getAttribute("Name"), new String[] {"test", "measurement", "crash" }));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorLike() throws Exception {
+
+		String filterString = "TestStep.Name lk 'test*'";
+		
+		Filter expected = Filter.and().add(LIKE.create(map.get("TestStep").getAttribute("Name"), "test*"));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorNotLike() throws Exception {
+
+		String filterString = "TestStep.Name not_lk 'test*'";
+		
+		Filter expected = Filter.and().add(NOT_LIKE.create(map.get("TestStep").getAttribute("Name"), "test*"));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	
+	@Test
+	public void testOperatorCIEqual() throws Exception {
+
+		String filterString = "TestStep.Name ci_eq 'test'";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	@Test
+	public void testOperatorCINotEqual() throws Exception {
+
+		String filterString = "TestStep.Name ci_ne 'test'";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_NOT_EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorCILessThan() throws Exception {
+
+		String filterString = "TestStep.LongAttribute ci_lt 10";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_LESS_THAN.create(map.get("TestStep").getAttribute("LongAttribute"), 10L));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorCILessThanOrEqual() throws Exception {
+
+		String filterString = "TestStep.LongAttribute ci_le 10";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_LESS_THAN_OR_EQUAL.create(map.get("TestStep").getAttribute("LongAttribute"), 10L));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorCIGreaterThan() throws Exception {
+
+		String filterString = "TestStep.LongAttribute ci_gt 10";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_GREATER_THAN.create(map.get("TestStep").getAttribute("LongAttribute"), 10L));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorCIGreaterThanOrEqual() throws Exception {
+
+		String filterString = "TestStep.LongAttribute ci_ge 10";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_GREATER_THAN_OR_EQUAL.create(map.get("TestStep").getAttribute("LongAttribute"), 10L));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorCIInSet() throws Exception {
+
+		String filterString = "TestStep.Name ci_in ('test', 'measurement', 'crash')";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_IN_SET.create(map.get("TestStep").getAttribute("Name"), new String[] {"test", "measurement", "crash" }));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorCINotInSet() throws Exception {
+
+		String filterString = "TestStep.Name ci_not_in ('test', 'measurement', 'crash')";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_NOT_IN_SET.create(map.get("TestStep").getAttribute("Name"), new String[] {"test", "measurement", "crash" }));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorCILike() throws Exception {
+
+		String filterString = "TestStep.Name ci_lk 'test*'";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_LIKE.create(map.get("TestStep").getAttribute("Name"), "test*"));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	@Test
+	public void testOperatorCINotLike() throws Exception {
+
+		String filterString = "TestStep.Name ci_not_lk 'test*'";
+		
+		Filter expected = Filter.and().add(CASE_INSENSITIVE_NOT_LIKE.create(map.get("TestStep").getAttribute("Name"), "test*"));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorIsNull() throws Exception {
+
+		String filterString = "TestStep.Name is_null";
+		
+		Filter expected = Filter.and().add(IS_NULL.create(map.get("TestStep").getAttribute("Name"), null));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	@Test
+	public void testOperatorIsNotNull() throws Exception {
+
+		String filterString = "TestStep.Name is_not_null";
+		
+		Filter expected = Filter.and().add(IS_NOT_NULL.create(map.get("TestStep").getAttribute("Name"), null));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOperatorBetween() throws Exception {
+
+		String filterString = "TestStep.LongAttribute bw (100, 1000)";
+		
+		Filter expected = Filter.and().add(BETWEEN.create(map.get("TestStep").getAttribute("LongAttribute"), new long[] { 100, 1000 }));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testInSetWithImplicitToStringConversion() throws Exception {
+
+		String filterString = "TestStep.Name in (12.2, 'test', 1)";
+		
+		Filter expected = Filter.and().add(IN_SET.create(map.get("TestStep").getAttribute("Name"), new String[] {"12.2", "test", "1" }));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test(expected = NumberFormatException.class)
+	public void testInSetWithStringForDoubleAttribute() throws Exception {
+
+		String filterString = "TestStep.DoubleAttribute in (12.2, 'Test', 1)";
+		
+		Filter expected = Filter.and().add(IN_SET.create(map.get("TestStep").getAttribute("DoubleAttribute"), new double[] { 12.2 }));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testInSetWithNumericDataTypes() throws Exception {
+
+		String filterString = "TestStep.DoubleAttribute in (12.2, -1.0, 0.006022)";
+		
+		Filter expected = Filter.and().add(IN_SET.create(map.get("TestStep").getAttribute("DoubleAttribute"), new double[] { 12.2, -1.0, 0.006022 }));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testSimple() throws Exception {
+
+		String filterString = "TestStep.Name eq 'test'";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"));
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	@Test
+	public void testSimpleWithBrackets() throws Exception {
+
+		String filterString = "(TestStep.Name eq 'test')";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testOr() throws Exception {
+	
+		String filterString = "(TestStep.Name eq 'test') or (Test.LongAttribute gt 1)";
+		
+		Filter expected = Filter.or()
+				.add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"))
+				.add(ComparisonOperator.GREATER_THAN.create(map.get("Test").getAttribute("LongAttribute"), 1L));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	@Test
+	public void testAnd() throws Exception {
+
+		String filterString = "(TestStep.Name eq 'test') and (Test.LongAttribute gt 1)";
+		
+		Filter expected = Filter.and()
+				.add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"))
+				.add(ComparisonOperator.GREATER_THAN.create(map.get("Test").getAttribute("LongAttribute"), 1L));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testAnd2() throws Exception {
+
+		String filterString = "TestStep.Name eq 'test' and Test.LongAttribute gt 1";
+		
+		Filter expected = Filter.and()
+				.add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"))
+				.add(ComparisonOperator.GREATER_THAN.create(map.get("Test").getAttribute("LongAttribute"), 1L));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testSimpleNot() throws Exception {
+
+		String filterString = "not(TestStep.Name eq 'test')";
+		
+		Filter expected = Filter.and()
+				.add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test")).invert();
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testNot() throws Exception {
+
+		String filterString = "TestStep.Name eq 'test' and not(Test.LongAttribute gt 1)";
+		
+		Filter expected = Filter.and()
+				.add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"))
+				.merge(Filter.or()
+						.add(ComparisonOperator.GREATER_THAN.create(map.get("Test").getAttribute("LongAttribute"), 1L))
+						.invert());
+
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	
+	@Test
+	public void testNested() throws Exception {
+
+		String filterString = "TestStep.Name eq 'test' and (Test.LongAttribute gt 1 or Test.IntegerAttribute lt 10)";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"))
+				.merge(Filter.or()
+						.add(ComparisonOperator.GREATER_THAN.create(map.get("Test").getAttribute("LongAttribute"), 1L))
+						.add(ComparisonOperator.LESS_THAN.create(map.get("Test").getAttribute("IntegerAttribute"), 10)));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testCaseOfKeywordsIsIgnored() throws Exception {
+
+		String filterString = "TestStep.Name Eq 'test' AND (Test.LongAttribute gt 1 oR Test.IntegerAttribute LT 10)";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"))
+				.merge(Filter.or()
+						.add(ComparisonOperator.GREATER_THAN.create(map.get("Test").getAttribute("LongAttribute"), 1L))
+						.add(ComparisonOperator.LESS_THAN.create(map.get("Test").getAttribute("IntegerAttribute"), 10)));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testAndPrecedence() throws Exception {
+
+		String filterString = "TestStep.Name eq 'test' and Test.LongAttribute gt 1 or Test.IntegerAttribute lt 10";
+		
+		Filter expected = Filter.or().merge(
+				Filter.and()
+					.add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"))
+					.add(ComparisonOperator.GREATER_THAN.create(map.get("Test").getAttribute("LongAttribute"), 1L)),
+				Filter.and()
+					.add(ComparisonOperator.LESS_THAN.create(map.get("Test").getAttribute("IntegerAttribute"), 10)));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testAndPrecedence2() throws Exception {
+
+		String filterString = "TestStep.Name eq 'test' or Test.LongAttribute gt 1 and Test.IntegerAttribute lt 10";
+		
+		Filter expected = Filter.or().merge(
+				Filter.and()
+					.add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test")),
+				Filter.and()
+					.add(ComparisonOperator.GREATER_THAN.create(map.get("Test").getAttribute("LongAttribute"), 1L))
+					.add(ComparisonOperator.LESS_THAN.create(map.get("Test").getAttribute("IntegerAttribute"), 10)));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testComplexFilter() throws Exception {
+	
+		String filterString = "TestStep.Name eq 'test' and (Test.LongAttribute gt 1 or Test.DoubleAttribute lt 12.3 or Test.IntegerAttribute eq 2) "
+				+ " and not(TestStep.FloatAttribute gt 3.14 or TestStep.BooleanAttribute eq true) or TestStep.DateAttribute gt '2017-01-02T01:02:03'";
+		
+		Filter expected = Filter.or().merge(
+				Filter.and().add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("Name"), "test"))
+					.merge(Filter.or()
+							.add(ComparisonOperator.GREATER_THAN.create(map.get("Test").getAttribute("LongAttribute"), 1L))
+							.add(ComparisonOperator.LESS_THAN.create(map.get("Test").getAttribute("DoubleAttribute"), 12.3))
+							.add(ComparisonOperator.EQUAL.create(map.get("Test").getAttribute("IntegerAttribute"), 2)),
+							Filter.or()
+								.add(ComparisonOperator.GREATER_THAN.create(map.get("TestStep").getAttribute("FloatAttribute"), 3.14f))
+								.add(ComparisonOperator.EQUAL.create(map.get("TestStep").getAttribute("BooleanAttribute"), true))
+								.invert()),
+				Filter.and().add(ComparisonOperator.GREATER_THAN.create(map.get("TestStep").getAttribute("DateAttribute"), LocalDateTime.parse("2017-01-02T01:02:03"))));
+					
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	@Test
+	public void testDataTypeString() throws Exception {
+		String filterString = "TestStep.Name eq 'Test'";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(
+						map.get("TestStep").getAttribute("Name"), 
+						"Test"));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testDataTypeDate() throws Exception {
+		String filterString = "TestStep.DateAttribute gt '2017-09-28T12:13:14'";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.GREATER_THAN.create(
+						map.get("TestStep").getAttribute("DateAttribute"), 
+						LocalDateTime.parse("2017-09-28T12:13:14")));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testDataTypeDateWithMillies() throws Exception {
+		String filterString = "TestStep.DateAttribute gt '2017-09-29T12:13:14.123456'";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.GREATER_THAN.create(
+						map.get("TestStep").getAttribute("DateAttribute"), 
+						LocalDateTime.parse("2017-09-29T12:13:14.123456")));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testDataTypeDateSequence() throws Exception {
+		String filterString = "TestStep.DateAttribute bw ('2017-09-28T12:13:14', '2017-09-29T17:18:19')";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.BETWEEN.create(
+						map.get("TestStep").getAttribute("DateAttribute"), 
+						new LocalDateTime[] {
+							LocalDateTime.parse("2017-09-28T12:13:14"),
+							LocalDateTime.parse("2017-09-29T17:18:19")
+						}));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testDataTypeBoolean() throws Exception {
+		String filterString = "TestStep.BooleanAttribute eq true";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(
+						map.get("TestStep").getAttribute("BooleanAttribute"), 
+						true));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	@Test
+	public void testDataTypeByte() throws Exception {
+		String filterString = "TestStep.ByteAttribute eq 127";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(
+						map.get("TestStep").getAttribute("ByteAttribute"), 
+						(byte) 127));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+
+	@Test
+	public void testDataTypeShort() throws Exception {
+		String filterString = "TestStep.ShortAttribute eq 1024";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(
+						map.get("TestStep").getAttribute("ShortAttribute"), 
+						(short) 1024));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testDataTypeInteger() throws Exception {
+		String filterString = "TestStep.IntegerAttribute eq " + Integer.MAX_VALUE;
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(
+						map.get("TestStep").getAttribute("IntegerAttribute"), 
+						Integer.MAX_VALUE));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testDataTypeLong() throws Exception {
+		String filterString = "TestStep.LongAttribute eq " + Long.MAX_VALUE;
+		
+		Filter expected = Filter.and().add(ComparisonOperator.EQUAL.create(
+						map.get("TestStep").getAttribute("LongAttribute"), 
+						Long.MAX_VALUE));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testDataTypeFloat() throws Exception {
+		String filterString = "TestStep.FloatAttribute gt 12.3";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.GREATER_THAN.create(
+						map.get("TestStep").getAttribute("FloatAttribute"), 
+						12.3f));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testDataTypeDouble() throws Exception {
+		String filterString = "TestStep.DoubleAttribute gt 12.2";
+		
+		Filter expected = Filter.and().add(ComparisonOperator.GREATER_THAN.create(
+						map.get("TestStep").getAttribute("DoubleAttribute"), 
+						12.2));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testKeywordsInValue() throws Exception {
+		String filterString = "Test.Name eq 'y eq z and not(x)'";
+		
+		Filter expected = Filter.and().add(EQUAL.create(
+						map.get("Test").getAttribute("Name"), 
+						"y eq z and not(x)"));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testEscapedQuotesInValue() throws Exception {
+		String filterString = "Test.Name eq 'Test \\'abc\\' 2'";
+		
+		Filter expected = Filter.and().add(EQUAL.create(
+						map.get("Test").getAttribute("Name"), 
+						"Test 'abc' 2"));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testEscapedDoubleQuotesInValue() throws Exception {
+		String filterString = "Test.Name eq 'Test \"abc\"'";
+		
+		Filter expected = Filter.and().add(EQUAL.create(
+						map.get("Test").getAttribute("Name"), 
+						"Test \"abc\""));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testBackslashInValue() throws Exception {
+		String filterString = "Test.Name eq 'c:\\Temp'";
+		
+		Filter expected = Filter.and().add(EQUAL.create(
+						map.get("Test").getAttribute("Name"), 
+						"c:\\Temp"));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testNewlineInValue() throws Exception {
+		String filterString = "Test.Name eq 'new\nline'";
+		
+		Filter expected = Filter.and().add(EQUAL.create(
+						map.get("Test").getAttribute("Name"), 
+						"new\nline"));
+		
+		assertThat(parseFilterString(entities, filterString)).isEqualTo(expected);
+	}
+	
+	@Test
+	public void testInvalidFilterStrings() {
+		SoftAssertions softly = new SoftAssertions();
+		
+		softly.assertThatThrownBy(() -> parseFilterString(entities, "xyz"))
+			.isInstanceOf(IllegalArgumentException.class)
+			.hasMessageStartingWith("Could not parse filter string");
+		
+		softly.assertThatThrownBy(() -> parseFilterString(entities, "and"))
+			.isInstanceOf(IllegalArgumentException.class)
+			.hasMessageStartingWith("Could not parse filter string");
+		
+		softly.assertThatThrownBy(() -> parseFilterString(entities, "x eq y"))
+			.isInstanceOf(IllegalArgumentException.class)
+			.hasMessageStartingWith("Could not parse filter string");
+		
+		softly.assertThatThrownBy(() -> parseFilterString(entities, "Test.Name eq y"))
+			.isInstanceOf(IllegalArgumentException.class)
+			.hasMessageStartingWith("Could not parse filter string");
+		
+		softly.assertThatThrownBy(() -> parseFilterString(entities, "((Test.Name eq 'y')"))
+			.isInstanceOf(IllegalArgumentException.class)
+			.hasMessageStartingWith("Could not parse filter string");
+		
+		softly.assertThatThrownBy(() -> parseFilterString(entities, "Test.Name eq 'y' and Test.Name lt 'x')"))
+			.isInstanceOf(IllegalArgumentException.class)
+			.hasMessageStartingWith("Could not parse filter string");
+
+		softly.assertThatThrownBy(() -> parseFilterString(entities, "x.y eq 'z'"))
+			.isInstanceOf(IllegalArgumentException.class)
+			.hasMessageStartingWith("Entity x not found in data source");
+		
+		softly.assertAll();
+	}
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/search/SearchActivityTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/search/SearchActivityTest.java
new file mode 100644
index 0000000..f6c7706
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/search/SearchActivityTest.java
@@ -0,0 +1,50 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control.search;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.businessobjects.control.SearchActivity;
+import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
+import org.junit.Test;
+
+public class SearchActivityTest {
+
+	@Test
+	public void testSearch() throws Exception {
+		SearchActivity activity = new SearchActivity();
+		List<TestStep> searchResult = activity.search(SearchMockHelper.createContextMock(), TestStep.class,
+				"TestStep.Name eq '*' ");
+		assertNotNull("search result list should not be null", searchResult);
+		assertEquals("The size of the search result list should be " + SearchMockHelper.ITEM_COUNT,
+				SearchMockHelper.ITEM_COUNT, searchResult.size());
+	}
+
+	@Test
+	public void listAvailableAttributes() throws Exception {
+		SearchActivity activity = new SearchActivity();
+		List<SearchAttribute> attributes = activity.listAvailableAttributes(SearchMockHelper.createContextMock(),
+				TestStep.class);
+		assertNotNull("test list should be not null", attributes);
+		assertEquals("The attributes list size should be " + SearchMockHelper.ITEM_COUNT, SearchMockHelper.ITEM_COUNT,
+				attributes.size());
+	}
+
+}
diff --git a/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/search/SearchMockHelper.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/search/SearchMockHelper.java
new file mode 100644
index 0000000..e5f77cf
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/businessobjects/control/search/SearchMockHelper.java
@@ -0,0 +1,166 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.businessobjects.control.search;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.Core;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.adapter.ModelManager;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.EnumRegistry;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.model.Value;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.Filter;
+import org.eclipse.mdm.api.base.search.SearchService;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+
+public class SearchMockHelper {
+
+	public static final int ITEM_COUNT = 27;
+	
+	private SearchMockHelper() {
+	}
+
+	public static ApplicationContext createContextMock() throws Exception {
+		ApplicationContext context = Mockito.mock(ApplicationContext.class);
+		List<EntityType> etResultMock = createETListMock();
+		Optional<SearchService> mockedSearchService = createSearchServiceMock(etResultMock);
+		when(context.getSearchService()).thenReturn(mockedSearchService);
+		Optional<ModelManager> mockedModelManager = createModelManagerMock(etResultMock);
+		when(context.getModelManager()).thenReturn(mockedModelManager);
+		
+		return context;
+	}
+
+	public static Optional<ModelManager> createModelManagerMock(List<EntityType> etResultMock) {
+		ModelManager modelManager = Mockito.mock(ModelManager.class);
+		
+		when(modelManager.getEntityType("Test")).thenReturn(etResultMock.get(0));
+		when(modelManager.getEntityType("TestStep")).thenReturn(etResultMock.get(1));
+		when(modelManager.getEntityType("Measurement")).thenReturn(etResultMock.get(2));
+		
+		return Optional.of(modelManager);
+	}
+	
+	public static Optional<SearchService> createSearchServiceMock(List<EntityType> etResultMock) throws Exception {
+		SearchService searchService = Mockito.mock(SearchService.class);
+		when(searchService.listEntityTypes(TestStep.class)).thenReturn(etResultMock);
+		List<Attribute> attrList = new ArrayList<>();
+		attrList.add(Mockito.mock(Attribute.class));
+		List<Entity> mockedSearchResult = createMockedSearchRes(TestStep.class, "TestStep");
+		when(searchService.fetch(any(), anyList(), any(Filter.class))).thenReturn(mockedSearchResult);
+		
+		return Optional.of(searchService);
+	}
+
+	public static <T extends Entity> List<Entity> createMockedSearchRes(Class<T> clazz, String entityTypeName)
+			throws Exception {
+
+		List<Entity> mockedRes = new ArrayList<>();
+
+		for (int i = 1; i <= ITEM_COUNT; i++) {
+			T etMock = createEntityMock(clazz, entityTypeName + "_" + i, "Environment", Integer.toString(i));
+			mockedRes.add(etMock);
+		}
+
+		return mockedRes;
+	}
+
+	public static List<EntityType> createETListMock() {
+		List<EntityType> mockedEntityList = new ArrayList<>();
+		EntityType mockedETTest = createEntityTypeMock("Test");
+		EntityType mockedETTestStep = createEntityTypeMock("TestStep");
+		EntityType mockedETMeasurement = createEntityTypeMock("Measurement");
+		mockedEntityList.add(mockedETTest);
+		mockedEntityList.add(mockedETTestStep);
+		mockedEntityList.add(mockedETMeasurement);
+		
+		return mockedEntityList;
+	}
+
+	private static EntityType createEntityTypeMock(String entityName) {
+		EntityType mockedETTest = Mockito.mock(EntityType.class);
+		List<Attribute> mockedAttributes = new ArrayList<>();
+		
+		// Initialize enums or else ValueType.name() returns null
+		EnumRegistry.getInstance();
+		
+		mockedAttributes.add(createAttributeMock(mockedETTest, "Name", ValueType.STRING));
+		mockedAttributes.add(createAttributeMock(mockedETTest, "DateAttribute", ValueType.DATE));
+		mockedAttributes.add(createAttributeMock(mockedETTest, "BooleanAttribute", ValueType.BOOLEAN));
+		mockedAttributes.add(createAttributeMock(mockedETTest, "ByteAttribute", ValueType.BYTE));
+		mockedAttributes.add(createAttributeMock(mockedETTest, "ShortAttribute", ValueType.SHORT));
+		mockedAttributes.add(createAttributeMock(mockedETTest, "IntegerAttribute", ValueType.INTEGER));
+		mockedAttributes.add(createAttributeMock(mockedETTest, "LongAttribute", ValueType.LONG));
+		mockedAttributes.add(createAttributeMock(mockedETTest, "FloatAttribute", ValueType.FLOAT));
+		mockedAttributes.add(createAttributeMock(mockedETTest, "DoubleAttribute", ValueType.DOUBLE));
+		
+		when(mockedETTest.getName()).thenReturn(entityName);
+		when(mockedETTest.getAttributes()).thenReturn(mockedAttributes);
+		when(mockedETTest.getAttribute(any())).thenAnswer((InvocationOnMock invocation) -> (Attribute) mockedAttributes.stream().filter(a -> a.getName().equals(invocation.getArgument(0))).findAny().get());
+		
+		return mockedETTest;
+	}
+	
+	private static Attribute createAttributeMock(EntityType entity, String attributeName, ValueType<?> valueType) 
+	{
+		Attribute attributeMock = Mockito.mock(Attribute.class);
+		when(attributeMock.getName()).thenReturn(attributeName);
+		when(attributeMock.getValueType()).thenReturn(valueType);
+		when(attributeMock.createValue(any())).thenCallRealMethod();
+		when(attributeMock.createValue(any(), any())).thenCallRealMethod();
+		when(attributeMock.createValue(any(), anyBoolean(), any())).thenCallRealMethod();
+		when(attributeMock.createValueSeq(any(), any())).thenCallRealMethod();
+		when(attributeMock.getEntityType()).thenReturn(entity);
+		
+		return attributeMock;
+	}
+
+	private static <T extends Entity> T createEntityMock(Class<T> type, String name, String sourceName, String id)
+			throws Exception {
+
+		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<T> constructor = type.getDeclaredConstructor(Core.class);
+		constructor.setAccessible(true);
+		T instance = constructor.newInstance(core);
+		constructor.setAccessible(false);
+		
+		return instance;
+	}
+
+	
+}
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..311b93d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/query/boundary/QueryServiceTest.java
@@ -0,0 +1,279 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.adapter.ModelManager;
+import org.eclipse.mdm.api.base.model.Test;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+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.search.SearchService;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+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 {
+
+		ApplicationContext context = mockContext();
+		ConnectorService connectorService = Mockito.mock(ConnectorService.class);
+		when(connectorService.getContextByName("env1")).thenReturn(context);
+
+		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("id1");
+		expectedRow.addColumns(
+				Arrays.asList(new Column("Test", "Id", "id1", null), new Column("Test", "Name", "Test-Name", null)));
+
+		assertThat(queryService.queryRows(request)).contains(expectedRow);
+
+	}
+
+	@org.junit.Test
+	public void testQueryMissingEnvironmentShouldByIgnored() throws DataAccessException {
+
+		ApplicationContext context = mockContext();
+		ConnectorService connectorService = Mockito.mock(ConnectorService.class);
+		when(connectorService.getContextByName("env1")).thenReturn(context);
+		doThrow(ConnectorServiceException.class).when(connectorService).getContextByName("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("id1");
+		expectedRow.addColumns(
+				Arrays.asList(new Column("Test", "Id", "id1", null), new Column("Test", "Name", "Test-Name", null)));
+
+		assertThat(queryService.queryRows(request)).contains(expectedRow);
+	}
+
+	@org.junit.Test
+	public void testQueryMissingEntitiesShouldBeIgnored() throws DataAccessException {
+
+		ApplicationContext context1 = mockContext();
+		ApplicationContext context2 = mockContext();
+		ConnectorService connectorService = Mockito.mock(ConnectorService.class);
+		when(connectorService.getContextByName("env1")).thenReturn(context1);
+		when(connectorService.getContextByName("env2")).thenReturn(context2);
+		
+		EntityType type = mockEntity("env2", "Test");
+		EntityType pool = mockEntity("env2", "Pool");
+
+		when(context2.getModelManager().get().getEntityType(Test.class)).thenReturn(type);
+		when(context2.getModelManager().get().getEntityType("Test")).thenReturn(type);
+		when(context2.getModelManager().get().getEntityType(Pool.class)).thenReturn(pool);
+		when(context2.getModelManager().get().getEntityType("Pool")).thenReturn(pool);
+
+		SearchService ss = mock(SearchService.class);
+		EntityType test = context2.getModelManager().get().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(context2.getModelManager().get().getEntityType(Test.class));
+		recordTest.addValue(ValueType.STRING.create("Id", "id1"));
+		recordTest.addValue(ValueType.STRING.create("Name", "Test-Name"));
+
+		Record recordPool = new Record(context2.getModelManager().get().getEntityType(Pool.class));
+		recordPool.addValue(ValueType.STRING.create("Name", "Pool-Name"));
+		Result result = new Result();
+		result.addRecord(recordPool);
+		result.addRecord(recordTest);
+		when(context2.getSearchService().get().fetchResults(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()))
+				.thenReturn(Arrays.asList(result));
+
+
+		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("id1");
+		expectedRowEnv1.addColumns(
+				Arrays.asList(new Column("Test", "Id", "id1", null), new Column("Test", "Name", "Test-Name", null)));
+
+		Row expectedRowEnv2 = new Row();
+		expectedRowEnv2.setSource("env2");
+		expectedRowEnv2.setType("Test");
+		expectedRowEnv2.setId("id1");
+		expectedRowEnv2.addColumns(Arrays.asList(new Column("Test", "Id", "id1", 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", "id1"),
+				new Tuple("env2", "Test", "id1"));
+
+		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 ApplicationContext mockContext() {
+		ModelManager mm = mockModelManager();
+		EntityManager em = Mockito.mock(EntityManager.class);
+		
+		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.STRING.create("Id", "id1"));
+		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));
+		
+		ApplicationContext context = Mockito.mock(ApplicationContext.class);
+		when(context.getEntityManager()).thenReturn(Optional.of(em));
+		when(context.getModelManager()).thenReturn(Optional.of(mm));
+		when(context.getSearchService()).thenReturn(Optional.of(ss));
+		return context;
+	}
+	
+	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 {
+		ApplicationContext context = mockContext();
+
+		Record record = new Record(context.getModelManager().get().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));
+		
+		org.eclipse.mdm.api.base.query.QueryService qs = mock(org.eclipse.mdm.api.base.query.QueryService.class);
+		when(qs.createQuery()).thenReturn(query);
+		when(context.getQueryService()).thenReturn(Optional.of(qs));
+
+		ConnectorService connectorService = Mockito.mock(ConnectorService.class);
+		when(connectorService.getContextByName("env1")).thenReturn(context);
+
+		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..4f93967
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/query/boundary/QueryTest.java
@@ -0,0 +1,137 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.query.boundary;
+
+import static org.eclipse.mdm.api.odsadapter.ODSContextFactory.PARAM_NAMESERVICE;
+import static org.eclipse.mdm.api.odsadapter.ODSContextFactory.PARAM_PASSWORD;
+import static org.eclipse.mdm.api.odsadapter.ODSContextFactory.PARAM_SERVICENAME;
+import static org.eclipse.mdm.api.odsadapter.ODSContextFactory.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.ServiceNotProvidedException;
+import org.eclipse.mdm.api.base.adapter.ModelManager;
+import org.eclipse.mdm.api.base.model.Value;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.base.query.QueryService;
+import org.eclipse.mdm.api.base.query.Record;
+import org.eclipse.mdm.api.base.query.Result;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.odsadapter.ODSContextFactory;
+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 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 String NAME_SERVICE = "corbaloc::1.2@%s:%s/NameService";
+
+	private static final String USER = "sa";
+	private static final String PASSWORD = "sa";
+
+	private static ApplicationContext context;
+	private static ModelManager modelManager;
+	private static org.eclipse.mdm.api.base.query.QueryService queryService;
+	
+	@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);
+
+		context = new ODSContextFactory().connect(connectionParameters);
+		modelManager = context.getModelManager()
+				.orElseThrow(() -> new ServiceNotProvidedException(ModelManager.class));
+		queryService = context.getQueryService()
+				.orElseThrow(() -> new ServiceNotProvidedException(QueryService.class));
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() throws ConnectionException {
+		if (context != null) {
+			context.close();
+		}
+	}
+
+	@org.junit.Test
+	@Ignore
+	public void test() throws DataAccessException, JsonGenerationException, JsonMappingException, IOException {
+		
+
+		List<Result> result = queryService.createQuery().select(modelManager.getEntityType("Test").getAttribute("Id"))
+				.select(modelManager.getEntityType("Test").getAttribute("Name"))
+				.select(modelManager.getEntityType("TestStep").getAttribute("Id"))
+				.select(modelManager.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.businessobjects/src/test/java/org/eclipse/mdm/shoppingbasket/boundary/ShoppingBasketResourceTest.java b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/shoppingbasket/boundary/ShoppingBasketResourceTest.java
new file mode 100644
index 0000000..7549f10
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/src/test/java/org/eclipse/mdm/shoppingbasket/boundary/ShoppingBasketResourceTest.java
@@ -0,0 +1,175 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.shoppingbasket.boundary;

+

+import static org.assertj.core.api.Assertions.assertThat;

+import static org.assertj.core.api.Assertions.fail;

+import static org.mockito.Mockito.when;

+

+import java.io.InputStream;

+import java.io.StringReader;

+import java.net.URISyntaxException;

+import java.util.Arrays;

+import java.util.Optional;

+

+import javax.ws.rs.client.Entity;

+import javax.ws.rs.core.Application;

+import javax.ws.rs.core.UriBuilder;

+import javax.xml.XMLConstants;

+import javax.xml.transform.stream.StreamSource;

+import javax.xml.validation.Schema;

+import javax.xml.validation.SchemaFactory;

+import javax.xml.validation.Validator;

+

+import org.assertj.core.groups.Tuple;

+import org.eclipse.mdm.api.base.model.Measurement;

+import org.eclipse.mdm.api.base.model.TestStep;

+import org.eclipse.mdm.api.base.query.DataAccessException;

+import org.eclipse.mdm.api.dflt.ApplicationContext;

+import org.eclipse.mdm.api.dflt.EntityManager;

+import org.eclipse.mdm.connector.boundary.ConnectorService;

+import org.eclipse.mdm.shoppingbasket.entity.MDMItem;

+import org.eclipse.mdm.shoppingbasket.entity.ShoppingBasket;

+import org.eclipse.mdm.shoppingbasket.entity.ShoppingBasketRequest;

+import org.glassfish.hk2.api.Factory;

+import org.glassfish.hk2.utilities.binding.AbstractBinder;

+import org.glassfish.jersey.jackson.JacksonFeature;

+import org.glassfish.jersey.server.ResourceConfig;

+import org.glassfish.jersey.test.JerseyTest;

+import org.junit.Before;

+import org.junit.Test;

+import org.mockito.Mockito;

+

+import com.google.common.collect.ImmutableMap;

+

+public class ShoppingBasketResourceTest extends JerseyTest {

+

+	private static ApplicationContext context = Mockito.mock(ApplicationContext.class);

+	private static EntityManager em = Mockito.mock(EntityManager.class);

+	private static ConnectorService connectorService = Mockito.mock(ConnectorService.class);

+

+	private static TestStep testStep = Mockito.mock(TestStep.class);

+	private static Measurement measurement = Mockito.mock(Measurement.class);

+

+	public static class ConnectorServiceFactory implements Factory<ConnectorService> {

+		@Override

+		public void dispose(ConnectorService connectorService) {

+			// nothing to do here

+		}

+

+		@Override

+		public ConnectorService provide() {

+			return connectorService;

+		}

+	}

+	

+	@Override

+	public Application configure() {

+		ResourceConfig config = new ResourceConfig();

+		

+		config.register(new AbstractBinder() {

+			@Override

+			protected void configure() {

+				bindFactory(ConnectorServiceFactory.class).to(ConnectorService.class);

+			}

+		});

+

+		config.register(ShoppingBasketResource.class);

+		config.register(JacksonFeature.class);

+		return config;

+	}

+

+	@Before

+	public void init() {

+

+		when(testStep.getSourceName()).thenReturn("MDMTEST");

+		when(testStep.getTypeName()).thenReturn("TestStep");

+		when(testStep.getID()).thenReturn("1");

+

+		when(measurement.getSourceName()).thenReturn("MDMTEST");

+		when(measurement.getTypeName()).thenReturn("Measurement");

+		when(measurement.getID()).thenReturn("2");

+

+		when(connectorService.getContextByName("MDMTEST")).thenReturn(context);

+		when(context.getEntityManager()).thenReturn(Optional.of(em));

+		when(em.load(TestStep.class, "1")).thenReturn(testStep);

+		when(context.getAdapterType()).thenReturn("ods");

+		when(em.getLinks(Mockito.anyList())).thenReturn(ImmutableMap.of(testStep, "servicename/asampath"));

+	}

+

+	@Test

+	public void testEmptyShoppingBasketRequest() {

+		ShoppingBasketRequest request = new ShoppingBasketRequest();

+

+		ShoppingBasket basket = target("shoppingbasket").request().post(Entity.json(request), ShoppingBasket.class);

+		assertThat(basket.getItems()).isEmpty();

+	}

+

+	@Test

+	public void testShoppingBasket() throws DataAccessException, URISyntaxException {

+		when(em.getLinks(Mockito.anyList()))

+				.thenReturn(ImmutableMap.of(testStep, "servicename/asampath/testStep", measurement,

+						"servicename/asampath/measurement"));

+

+		ShoppingBasket basket = target("shoppingbasket").request()

+				.post(Entity.json(getShoppingBasketRequest()), ShoppingBasket.class);

+

+		assertThat(basket.getName()).isEqualTo("my shopping basket (<äöüß\\/>)");

+		assertThat(basket.getItems())

+				.extracting(i -> Tuple.tuple(i.getSource(), i.getLink(), i.getRestURI()))

+				.containsExactly(

+						Tuple.tuple("ods", "servicename/asampath/testStep",

+								UriBuilder.fromUri(this.getBaseUri()).path("/environments/MDMTEST/teststeps/1")

+										.build()),

+						Tuple.tuple("ods", "servicename/asampath/measurement", UriBuilder.fromUri(this.getBaseUri())

+								.path("/environments/MDMTEST/measurements/2").build()));

+	}

+

+	@Test

+	public void testValidateShoppingBasketXml() {

+		String xml = target("shoppingbasket").request().post(Entity.json(getShoppingBasketRequest()), String.class);

+		validateAgainstXSD(xml, ShoppingBasketResourceTest.class.getResourceAsStream("/shoppingbasket1.0.xsd"));

+		System.out.println(xml);

+	}

+

+	private ShoppingBasketRequest getShoppingBasketRequest() {

+		MDMItem item1 = new MDMItem();

+		item1.setSource("MDMTEST");

+		item1.setType("TestStep");

+		item1.setId("1");

+

+		MDMItem item2 = new MDMItem();

+		item2.setSource("MDMTEST");

+		item2.setType("Measurement");

+		item2.setId("2");

+

+		ShoppingBasketRequest request = new ShoppingBasketRequest();

+		request.setName("my shopping basket (<äöüß\\/>)");

+		request.setItems(Arrays.asList(item1, item2));

+		return request;

+	}

+

+	private void validateAgainstXSD(String xml, InputStream xsd) {

+		try {

+			SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

+			Schema schema = factory.newSchema(new StreamSource(xsd));

+			Validator validator = schema.newValidator();

+			validator.validate(new StreamSource(new StringReader(xml)));

+		} catch (Exception ex) {

+			fail("XML could not be validated: " + xml, ex);

+		}

+	}

+

+}

diff --git a/org.eclipse.mdm.businessobjects/target/generated-sources/antlr4/org/eclipse/mdm/businessobjects/filter/.gitignore b/org.eclipse.mdm.businessobjects/target/generated-sources/antlr4/org/eclipse/mdm/businessobjects/filter/.gitignore
new file mode 100644
index 0000000..651663d
--- /dev/null
+++ b/org.eclipse.mdm.businessobjects/target/generated-sources/antlr4/org/eclipse/mdm/businessobjects/filter/.gitignore
@@ -0,0 +1,8 @@
+/FilterGrammar.tokens
+/FilterGrammarBaseListener.java
+/FilterGrammarBaseVisitor.java
+/FilterGrammarLexer.java
+/FilterGrammarLexer.tokens
+/FilterGrammarListener.java
+/FilterGrammarParser.java
+/FilterGrammarVisitor.java
diff --git a/org.eclipse.mdm.connector/build.gradle b/org.eclipse.mdm.connector/build.gradle
new file mode 100644
index 0000000..6998fa6
--- /dev/null
+++ b/org.eclipse.mdm.connector/build.gradle
@@ -0,0 +1,32 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+description = 'MDM Connector'
+
+dependencies {
+	compile "org.eclipse.mdm:org.eclipse.mdm.api.base:${version}"
+	compile "org.eclipse.mdm:org.eclipse.mdm.api.default:${version}"
+	compile project(':org.eclipse.mdm.property')
+	compile group: 'com.google.guava', name: 'guava', version: "25.0-jre"
+
+	compileOnly 'javax:javaee-api:7.0'
+
+	runtime "org.eclipse.mdm:org.eclipse.mdm.api.odsadapter:${version}"
+}
+
+jar {
+	metaInf { from '../NOTICE.txt' }
+	metaInf { from '../LICENSE.txt' }
+}
diff --git a/org.eclipse.mdm.connector/src/main/configuration/service.xml b/org.eclipse.mdm.connector/src/main/configuration/service.xml
new file mode 100644
index 0000000..5c2bd62
--- /dev/null
+++ b/org.eclipse.mdm.connector/src/main/configuration/service.xml
@@ -0,0 +1,45 @@
+<!-- 
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ -->
+
+<services>
+	<service entityManagerFactoryClass="org.eclipse.mdm.api.odsadapter.ODSContextFactory">
+		<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.ODSContextFactory">
+		<param name="nameservice">corbaloc::1.2@YOUR_HOST2:2809/NameService</param>
+		<param name="servicename">YOUR_SERVICE2.ASAM-ODS</param>
+		
+		<!-- Specify the credentials of the technical user which opens sessions on behalf of the login user -->
+		<param name="user">sa</param>
+		<param name="password">sa</param>
+		
+		<!--Activeate indexing on Peak ODS Server -->
+		<param name="freetext.active">true</param>
+		<param name="freetext.notificationType">peak</param>
+		<param name="freetext.notificationUrl">http://YOUR_HOST2:8089/api</param>
+	</service>
+	<service entityManagerFactoryClass="org.eclipse.mdm.api.odsadapter.ODSContextFactory">
+		<param name="nameservice">corbaloc::1.2@YOUR_HOST3:2809/NameService</param>
+		<param name="servicename">YOUR_SERVICE3.ASAM-ODS</param>
+		
+		<!-- Specify the credentials of the technical user which opens sessions on behalf of the login user -->
+		<param name="user">sa</param>
+		<param name="password">sa</param>
+		
+		<!--Activate indexing on a Avalon server -->
+		<param name="freetext.active">true</param>
+		<param name="freetext.notificationType">avalon</param>
+		<param name="freetext.pollingInterval">5000</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
new file mode 100644
index 0000000..83201ab
--- /dev/null
+++ b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/boundary/ConnectorService.java
@@ -0,0 +1,197 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.connector.boundary;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.enterprise.context.SessionScoped;
+import javax.inject.Inject;
+import javax.security.auth.spi.LoginModule;
+
+import org.eclipse.mdm.api.base.ConnectionException;
+import org.eclipse.mdm.api.base.ServiceNotProvidedException;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.ApplicationContextFactory;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.connector.control.ServiceConfigurationActivity;
+import org.eclipse.mdm.connector.entity.ServiceConfiguration;
+import org.eclipse.mdm.property.GlobalProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+/**
+ * ConnectorServcie Bean implementation to create and close connections
+ *
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ * @author Canoo Engineering (removal of hardcoded ODS dependencies)
+ *
+ */
+@SessionScoped
+public class ConnectorService implements Serializable {
+
+	private static final Logger LOG = LoggerFactory.getLogger(ConnectorService.class);
+	private static final String CONNECTION_PARAM_FOR_USER = "for_user";
+
+	@Inject
+	Principal principal;
+
+	@Inject
+	ServiceConfigurationActivity serviceConfigurationActivity;
+
+	@Inject
+	@GlobalProperty
+	private Map<String, String> globalProperties = Collections.emptyMap();
+
+	private List<ApplicationContext> contexts = Lists.newArrayList();
+	
+	public ConnectorService() {
+		// empty constructor for CDI
+	}
+	
+	
+	/**
+	 * Creates a connector service for usage outside the session scope of CDI.
+	 * 
+	 * @param principal Principal the connector service uses.
+	 * @param globalProperties global properties supplied the opened application contexts.
+	 */
+	public ConnectorService(Principal principal, Map<String, String> globalProperties) {
+		super();
+		this.principal = principal;
+		this.serviceConfigurationActivity = new ServiceConfigurationActivity();
+		this.globalProperties = globalProperties;
+	}
+
+
+	/**
+	 * returns all available {@link ApplicationContext}s
+	 *
+	 * @return list of available {@link ApplicationContext}s
+	 */
+	public List<ApplicationContext> getContexts() {
+		return ImmutableList.copyOf(contexts);
+	}
+
+	/**
+	 * returns an {@link ApplicationContext} identified by the given name
+	 *
+	 * @param name
+	 *            source name (e.g. MDM {@link Environment} name)
+	 * @return the matching {@link ApplicationContext}
+	 */
+	public ApplicationContext getContextByName(String name) {
+		try {
+			for (ApplicationContext context : getContexts()) {
+				String sourceName = context.getEntityManager()
+						.orElseThrow(() -> new ServiceNotProvidedException(EntityManager.class))
+						.loadEnvironment().getSourceName();
+
+				if (sourceName.equals(name)) {
+					return context;
+				}
+			}
+			String errorMessage = "no data source with environment name '" + name + "' connected!";
+			throw new ConnectorServiceException(errorMessage);
+
+		} catch (DataAccessException e) {
+			throw new ConnectorServiceException(e.getMessage(), e);
+		}
+
+	}
+
+
+	@PostConstruct
+	public void connect() {
+		LOG.info("connecting user with name '" + principal.getName() + "'");
+		this.contexts = serviceConfigurationActivity
+				.readServiceConfigurations().stream()
+				.map(this::connectContexts)
+				.filter(Optional::isPresent)
+				.map(Optional::get)
+				.collect(Collectors.toList());
+	}
+
+	/**
+	 * disconnect from all connected data sources
+	 * This method is call from a {@link LoginModule} at logout
+	 *
+	 * This method is call from a {@link LoginModule}
+	 *
+     */
+	@PreDestroy
+    public void disconnect() {
+        disconnectContexts(contexts);
+        LOG.info("user with name '" + principal.getName() + "' has been disconnected!");
+    }
+
+	private Optional<ApplicationContext> connectContexts(ServiceConfiguration source) {
+		try {
+
+			Class<? extends ApplicationContextFactory> contextFactoryClass = Thread.currentThread()
+					.getContextClassLoader().loadClass(source.getContextFactoryClass())
+					.asSubclass(ApplicationContextFactory.class);
+			ApplicationContextFactory contextFactory = contextFactoryClass.newInstance();
+
+			Map<String, String> connectionParameters = new HashMap<>();
+			connectionParameters.putAll(globalProperties);
+			connectionParameters.putAll(source.getConnectionParameters());
+			connectionParameters.put(CONNECTION_PARAM_FOR_USER, principal.getName());
+
+			ApplicationContext context = contextFactory.connect(connectionParameters);
+			return Optional.of(context);
+
+		} catch (ConnectionException e) {
+			LOG.warn("unable to logon user with name '" + principal.getName() + "' at data source '" + source.toString()
+					+ "' (reason: " + e.getMessage() + ")");
+		} catch (Exception e) {
+			LOG.error("failed to initialize entity manager using factory '" + source.getContextFactoryClass()
+					+ "' (reason: " + e + ")", e);
+		}
+		return Optional.empty();
+	}
+
+	private static void disconnectContexts(List<ApplicationContext> contextList) {
+		for (ApplicationContext context : contextList) {
+			disconnectContext(context);
+		}
+	}
+
+	private static void disconnectContext(ApplicationContext context) {
+		try {
+			if (context != null) {
+				context.close();
+			}
+		} catch (ConnectionException e) {
+			LOG.error("unable to logout user from MDM datasource (reason: " + e.getMessage() + ")");
+			throw new ConnectorServiceException(e.getMessage(), e);
+		}
+	}
+
+}
diff --git a/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/boundary/ConnectorServiceException.java b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/boundary/ConnectorServiceException.java
new file mode 100644
index 0000000..ab0f0a2
--- /dev/null
+++ b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/boundary/ConnectorServiceException.java
@@ -0,0 +1,30 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.connector.boundary;
+
+public class ConnectorServiceException extends RuntimeException {
+
+	private static final long serialVersionUID = 3491930665127242286L;
+
+	public ConnectorServiceException(String message) {
+		super(message);
+	}
+
+	public ConnectorServiceException(String message, Throwable t) {
+		super(message, t);
+	}
+
+}
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
new file mode 100644
index 0000000..057e87b
--- /dev/null
+++ b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/control/ServiceConfigurationActivity.java
@@ -0,0 +1,135 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.connector.control;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+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 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.NodeList;
+
+/**
+ * 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 Logger LOG   = LoggerFactory.getLogger(ServiceConfigurationActivity.class);
+
+	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_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() {
+		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)));
+			}
+
+			if(parsedServiceElements.size() == 0) {
+				LOG.warn("No service connectors are configured. This is probably wrong!");
+				throw new IllegalStateException("No configured connectors!");
+			}
+			return parsedServiceElements;
+
+		} catch (ConnectorServiceException e) {
+			LOG.error("Could not create connector service", e);
+			throw e;
+		} catch (Exception e) {
+			LOG.error("Could not create connector service", e);
+			throw new ConnectorServiceException(e.toString(), e);
+		}
+	}
+
+	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 static String readElementAttribute(Element element, String attrName) {
+		String value = element.getAttribute(attrName);
+		if (value.trim().isEmpty()) {
+			throw new ConnectorServiceException(
+					"mandatory attribute '" + attrName + "' of element '" + element.getNodeName() + "' is missing!");
+		}
+		return value;
+	}
+
+	private static File getServiceFile() {
+		File file = new File(COMPONENT_CONFIG_ROOT_FOLDER);
+		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()) {
+			throw new ConnectorServiceException(
+					"mandatory service configuration file at '" + serviceXML.getAbsolutePath() + "' does not exist!");
+		}
+		return serviceXML;
+	}
+
+}
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
new file mode 100644
index 0000000..c8680e4
--- /dev/null
+++ b/org.eclipse.mdm.connector/src/main/java/org/eclipse/mdm/connector/entity/ServiceConfiguration.java
@@ -0,0 +1,91 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+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 {
+
+	/**
+	 * The fully qualified class name of the entity manager factory for this
+	 * backend, never null.
+	 */
+	private final String contextFactoryClass;
+
+	/**
+	 * An unmodifiable map holding the connection parameters for this backend;
+	 * never null, but possibly empty.
+	 */
+	private final Map<String, String> connectionParameters;
+
+	/**
+	 * 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 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 contextFactoryClass, Map<String, String> connectionParameters) {
+		this.contextFactoryClass = Objects.requireNonNull(contextFactoryClass,
+				"Null \"contextFactoryClass\" 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 getContextFactoryClass() {
+		return contextFactoryClass;
+	}
+
+	/**
+	 * 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.contextFactoryClass + "#" + connectionParameters;
+	}
+
+}
diff --git a/org.eclipse.mdm.connector/src/test/java/org/eclipse/mdm/connector/boundary/ConnectorServiceTest.java b/org.eclipse.mdm.connector/src/test/java/org/eclipse/mdm/connector/boundary/ConnectorServiceTest.java
new file mode 100644
index 0000000..8dbc375
--- /dev/null
+++ b/org.eclipse.mdm.connector/src/test/java/org/eclipse/mdm/connector/boundary/ConnectorServiceTest.java
@@ -0,0 +1,170 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.connector.boundary;
+
+import org.eclipse.mdm.api.base.ConnectionException;
+import org.eclipse.mdm.api.base.adapter.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.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.ApplicationContextFactory;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.connector.control.ServiceConfigurationActivity;
+import org.eclipse.mdm.connector.entity.ServiceConfiguration;
+import org.junit.Test;
+
+import javax.ejb.SessionContext;
+import java.lang.reflect.Constructor;
+import java.security.Principal;
+import java.util.*;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * JUNIT Test for {@link ConnectorService}
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ * @author Canoo Engineering (more tests)
+ *
+ */
+@SuppressWarnings("javadoc")
+public class ConnectorServiceTest {
+
+	private final Principal testUser = new SimplePrincipal("testUser");
+	private static final String testSourceName = "testSource";
+	private final String differentSourceName = "differentSource";
+
+	@Test
+	public void testGetEntityManagers_happyFlow() throws Exception {
+		ConnectorService connectorService = createConnectorService(testUser);
+		connectorService.connect();
+		assertThat(connectorService.getContexts().size(), is(1));
+	}
+
+	@Test(expected = ConnectorServiceException.class)
+	public void testGetEntityManagerByName_differentSourceName() throws Exception {
+		ConnectorService connectorService = createConnectorService(testUser);
+		connectorService.connect();
+		connectorService.getContextByName(differentSourceName);
+	}
+
+	@Test
+	public void testGetEntityManagerByName_happyFlow() throws Exception {
+		ConnectorService connectorService = createConnectorService(testUser);
+		connectorService.connect();
+		assertNotNull(connectorService.getContextByName(testSourceName));
+	}
+
+	@Test
+	public void testDisconnect() throws Exception {
+		ConnectorService connectorService = createConnectorService(testUser);
+		connectorService.disconnect();
+	}
+
+	private static final class SimplePrincipal implements Principal {
+		private final String name;
+
+		SimplePrincipal(String name) {
+			this.name = Objects.requireNonNull(name);
+		}
+
+		@Override
+		public String getName() {
+			return name;
+		}
+
+		@Override
+		public boolean equals(Object obj) {
+			return (obj instanceof SimplePrincipal && ((SimplePrincipal) obj).name.equals(name));
+		}
+
+		@Override
+		public int hashCode() {
+			return name.hashCode();
+		}
+
+		@Override
+		public String toString() {
+			return name;
+		}
+
+	}
+
+	private static ConnectorService createConnectorService(Principal user) throws Exception {
+
+		SessionContext sessionContextMock = mock(SessionContext.class);
+		when(sessionContextMock.getCallerPrincipal()).thenReturn(user);
+
+		ServiceConfiguration serviceConfiguration = new ServiceConfiguration(TestContextFactory.class.getName(), Collections.emptyMap());
+
+		ConnectorService connectorService = new ConnectorService();
+
+		connectorService.principal = user;
+
+		ServiceConfigurationActivity serviceConfigurationActivity = mock(ServiceConfigurationActivity.class);
+		when(serviceConfigurationActivity.readServiceConfigurations()).thenReturn(Collections.singletonList(serviceConfiguration));
+		connectorService.serviceConfigurationActivity = serviceConfigurationActivity;
+
+		return connectorService;
+	}
+
+	public static final class TestContextFactory implements ApplicationContextFactory {
+
+		@Override
+		public ApplicationContext connect(Map<String, String> connectionParameters) throws ConnectionException {
+			return createContext(testSourceName);
+		}
+	}
+
+
+	private static ApplicationContext createContext(String sourceName) {
+		Environment env = createEntityMock(Environment.class, "MDMTest", sourceName, "1");
+
+		EntityManager em = mock(EntityManager.class);
+		when(em.loadEnvironment()).thenReturn(env);
+
+		ApplicationContext ctx = mock(ApplicationContext.class);
+		when(ctx.getEntityManager()).thenReturn(Optional.of(em));
+
+		return ctx;
+	}
+
+	private static <T extends Entity> T createEntityMock(Class<T> type, String name, String sourceName, String id) {
+
+		Map<String, Value> entityAttributes = new HashMap<>();
+		entityAttributes.put("Name", ValueType.STRING.create("Name", name));
+
+		Core core = mock(Core.class);
+		when(core.getSourceName()).thenReturn(sourceName);
+		when(core.getValues()).thenReturn(entityAttributes);
+		when(core.getID()).thenReturn(id);
+
+		try {
+			Constructor<T> constructor = type.getDeclaredConstructor(Core.class);
+			constructor.setAccessible(true);
+			return constructor.newInstance(core);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/build.gradle b/org.eclipse.mdm.filerelease/build.gradle
new file mode 100644
index 0000000..22702ca
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/build.gradle
@@ -0,0 +1,39 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+description = 'file release component'
+
+apply plugin: 'java'
+apply plugin: 'maven'
+apply plugin: 'eclipse'
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+
+repositories {
+	mavenLocal()
+	mavenCentral()
+}
+
+
+dependencies {
+	compile project (':org.eclipse.mdm.connector')
+	compile project (':org.eclipse.mdm.property')
+}
+
+jar {
+	metaInf { from '../NOTICE.txt' }
+	metaInf { from '../LICENSE.txt' }
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/configuration/BESA/model.atfx.technical b/org.eclipse.mdm.filerelease/src/main/configuration/BESA/model.atfx.technical
new file mode 100644
index 0000000..43ab7a0
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/configuration/BESA/model.atfx.technical
@@ -0,0 +1,17 @@
+<!-- ********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ******************************************************************************** -->
+<?xml version="1.0" encoding="utf-8"?>
+
+<atfx_file xmlns="http://www.asam.net/ODS/5.1/Schema" version="atfx_file: V1.1.0" xmlns:ext="urn:schemas-microsoft-com:xslt" xmlns:rdb="http://www.muellerbbm-vas.de/ods/rdb" xmlns:utils="java:vas.xml.Util"><base_model_version>asam29</base_model_version><application_model><application_enumeration><name>axistype</name><item><name>Xaxis</name><value>0</value></item><item><name>Yaxis</name><value>1</value></item><item><name>Both</name><value>2</value></item></application_enumeration><application_enumeration><name>coordinate_system_types</name><item><name>Cartesian</name><value>0</value></item><item><name>Polar</name><value>1</value></item><item><name>Cylindric</name><value>2</value></item></application_enumeration><application_enumeration><name>location_modes</name><item><name>Fixed</name><value>0</value></item><item><name>Varying</name><value>1</value></item></application_enumeration><application_element><name>env</name><basetype>AoEnvironment</basetype><application_attribute><name>env_iid</name><base_attribute>id</base_attribute><rdb:column>ENV_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>skill</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>SKILL</rdb:column></application_attribute><application_attribute><name>id</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>ID</rdb:column></application_attribute><application_attribute><name>touched</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>TOUCHED</rdb:column></application_attribute><application_attribute><name>description</name><base_attribute>description</base_attribute><length>4000</length><rdb:column>DESCR</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>VERSION</rdb:column></application_attribute><relation_attribute><name>prj_iid</name><ref_to>prj</ref_to><base_relation>tests</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>PROJECT__.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>ins_iid</name><ref_to>ins</ref_to><base_relation>equipments</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>SETUP_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>uuts</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>FREMDMOTOR_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>uuts</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>MOTOR_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>messtext_iid</name><ref_to>messtext</ref_to><base_relation>sequences</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>MESSTEXT_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>randbedingung_iid</name><ref_to>randbedingung</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>RANDBEDINGUNG_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>messzyklus_iid</name><ref_to>messzyklus</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>MESSZYKLUS_.ENV_IID IN (?)</rdb:join></relation_attribute><rdb:aid>1</rdb:aid><rdb:table>ENVIRONMENT_</rdb:table></application_element><application_element><name>dim</name><basetype>AoPhysicalDimension</basetype><application_attribute><name>dim_iid</name><base_attribute>id</base_attribute><rdb:column>DIM_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>meter_exp</name><base_attribute>length_exp</base_attribute><rdb:column>LEN</rdb:column></application_attribute><application_attribute><name>kilogramm_exp</name><base_attribute>mass_exp</base_attribute><rdb:column>MASS</rdb:column></application_attribute><application_attribute><name>second_exp</name><base_attribute>time_exp</base_attribute><rdb:column>TIM</rdb:column></application_attribute><application_attribute><name>ampere_exp</name><base_attribute>current_exp</base_attribute><rdb:column>CUR</rdb:column></application_attribute><application_attribute><name>kelvin_exp</name><base_attribute>temperature_exp</base_attribute><rdb:column>T</rdb:column></application_attribute><application_attribute><name>mol_exp</name><base_attribute>molar_amount_exp</base_attribute><rdb:column>PART</rdb:column></application_attribute><application_attribute><name>candela_exp</name><base_attribute>luminous_intensity_exp</base_attribute><rdb:column>LUM</rdb:column></application_attribute><application_attribute><name>angle</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>ANGLE</rdb:column></application_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>units</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dim_iid</inverse_name><rdb:join>UNIT_.DIM_IID IN (?)</rdb:join></relation_attribute><rdb:aid>2</rdb:aid><rdb:table>DIMENSION_</rdb:table></application_element><application_element><name>gru</name><basetype>AoUnitGroup</basetype><application_attribute><name>gru_iid</name><base_attribute>id</base_attribute><rdb:column>GRU_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>units</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>gru_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>UNIT_.UNT_IID=.UNT_IID AND .GRU_IID IN (?)</rdb:join></relation_attribute><rdb:aid>3</rdb:aid><rdb:table>UNITSYSTEM_</rdb:table></application_element><application_element><name>unt</name><basetype>AoUnit</basetype><application_attribute><name>unt_iid</name><base_attribute>id</base_attribute><rdb:column>UNT_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>offset</name><base_attribute>offset</base_attribute><rdb:column>OFFSET</rdb:column></application_attribute><application_attribute><name>factor</name><base_attribute>factor</base_attribute><rdb:column>FACTOR</rdb:column></application_attribute><application_attribute><name>dB</name><datatype>DT_SHORT</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>DBFLG</rdb:column></application_attribute><application_attribute><name>dB_reference_factor</name><datatype>DT_FLOAT</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>DBREF</rdb:column></application_attribute><relation_attribute><name>dim_iid</name><ref_to>dim</ref_to><base_relation>phys_dimension</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>unt_iid</inverse_name><rdb:column>DIM_IID</rdb:column><rdb:join>UNIT_.UNT_IID IN (?) AND UNIT_.DIM_IID=DIMENSION_.DIM_IID</rdb:join></relation_attribute><relation_attribute><name>quantity_iid</name><ref_to>quantity</ref_to><base_relation>quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>unt_iid</inverse_name><rdb:join>QUANTITY_.UNT_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><base_relation>measurement_quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>unt_iid</inverse_name><rdb:join>MEASURING QUANTITY_.UNT_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>par_iid</name><ref_to>par</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>unt_iid</inverse_name><rdb:join>PARAMETER_.UNT_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>gru_iid</name><ref_to>gru</ref_to><base_relation>groups</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>unt_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>UNITSYSTEM_.GRU_IID=.GRU_IID AND .UNT_IID IN (?)</rdb:join></relation_attribute><rdb:aid>4</rdb:aid><rdb:table>UNIT_</rdb:table></application_element><application_element><name>grq</name><basetype>AoQuantityGroup</basetype><application_attribute><name>grq_iid</name><base_attribute>id</base_attribute><rdb:column>GRQ_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>quantity_iid</name><ref_to>quantity</ref_to><base_relation>quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>grq_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>QUANTITY_.QUANTITY_IID=.QUANTITY_IID AND .GRQ_IID IN (?)</rdb:join></relation_attribute><rdb:aid>5</rdb:aid><rdb:table>QUANTITYSYSTEM_</rdb:table></application_element><application_element><name>prj</name><basetype>AoTest</basetype><application_attribute><name>prj_iid</name><base_attribute>id</base_attribute><rdb:column>PRJ_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>prj_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>PROJECT__.PRJ_IID IN (?) AND PROJECT__.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>tstser_iid</name><ref_to>tstser</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>prj_iid</inverse_name><rdb:join>TEST_.PRJ_IID IN (?)</rdb:join></relation_attribute><rdb:aid>6</rdb:aid><rdb:table>PROJECT__</rdb:table></application_element><application_element><name>tstser</name><basetype>AoSubTest</basetype><application_attribute><name>tstser_iid</name><base_attribute>id</base_attribute><rdb:column>TSTSER_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>mime_type</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><relation_attribute><name>prj_iid</name><ref_to>prj</ref_to><base_relation>parent_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>tstser_iid</inverse_name><rdb:column>PRJ_IID</rdb:column><rdb:join>TEST_.TSTSER_IID IN (?) AND TEST_.PRJ_IID=PROJECT__.PRJ_IID</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>tstser_iid</inverse_name><rdb:join>MEASUREMENT_.TSTSER_IID IN (?)</rdb:join></relation_attribute><rdb:aid>7</rdb:aid><rdb:table>TEST_</rdb:table></application_element><application_element><name>ins</name><basetype>AoTestEquipment</basetype><application_attribute><name>ins_iid</name><base_attribute>id</base_attribute><rdb:column>INS_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>setting</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>SETTING</rdb:column><rdb:sql>SELECT BLOB FROM SETUP_ WHERE INS_IID=?</rdb:sql></application_attribute><application_attribute><name>ramp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>RAMP</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>ins_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>SETUP_.INS_IID IN (?) AND SETUP_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>devchain_iid</name><ref_to>devchain</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>ins_iid</inverse_name><rdb:join>MEAS.DEV.CHAIN_.INS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>ins_iid</inverse_name><rdb:join>MEASUREMENT_.INS_IID IN (?)</rdb:join></relation_attribute><rdb:aid>8</rdb:aid><rdb:table>SETUP_</rdb:table></application_element><application_element><name>devchain</name><basetype>AoTestEquipmentPart</basetype><application_attribute><name>devofs</name><datatype>DT_FLOAT</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>DEVOFS</rdb:column></application_attribute><application_attribute><name>devchain_iid</name><base_attribute>id</base_attribute><rdb:column>DEVCHAIN_IID</rdb:column></application_attribute><application_attribute><name>smod1inp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1INP</rdb:column></application_attribute><application_attribute><name>smod2typ</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2TYP</rdb:column></application_attribute><application_attribute><name>smod2sno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2SNO</rdb:column></application_attribute><application_attribute><name>smod2pro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2PRO</rdb:column></application_attribute><application_attribute><name>smod2st</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2ST</rdb:column></application_attribute><application_attribute><name>smod2inp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2INP</rdb:column></application_attribute><application_attribute><name>imodinp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODINP</rdb:column></application_attribute><application_attribute><name>dev</name><datatype>DT_FLOAT</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>DEV</rdb:column></application_attribute><application_attribute><name>ctrltyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CTRLTYP</rdb:column></application_attribute><application_attribute><name>ctrlsno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CTRLSNO</rdb:column></application_attribute><application_attribute><name>ctrlpro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CTRLPRO</rdb:column></application_attribute><application_attribute><name>ctrlst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CTRLST</rdb:column></application_attribute><application_attribute><name>modtyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MODTYP</rdb:column></application_attribute><application_attribute><name>modsno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MODSNO</rdb:column></application_attribute><application_attribute><name>modpro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MODPRO</rdb:column></application_attribute><application_attribute><name>modst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MODST</rdb:column></application_attribute><application_attribute><name>imodtyp</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>IMODTYP</rdb:column></application_attribute><application_attribute><name>imodsno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODSNO</rdb:column></application_attribute><application_attribute><name>imodpro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODPRO</rdb:column></application_attribute><application_attribute><name>imodst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODST</rdb:column></application_attribute><application_attribute><name>imodswreva</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODSWREVA</rdb:column></application_attribute><application_attribute><name>imodswrevb</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODSWREVB</rdb:column></application_attribute><application_attribute><name>smod1typ</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1TYP</rdb:column></application_attribute><application_attribute><name>smod1sno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1SNO</rdb:column></application_attribute><application_attribute><name>smod1pro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1PRO</rdb:column></application_attribute><application_attribute><name>smod1st</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1ST</rdb:column></application_attribute><application_attribute><name>senstyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SENSTYP</rdb:column></application_attribute><application_attribute><name>senssno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SENSSNO</rdb:column></application_attribute><application_attribute><name>senspro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SENSPRO</rdb:column></application_attribute><application_attribute><name>sensst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SENSST</rdb:column></application_attribute><relation_attribute><name>ins_iid</name><ref_to>ins</ref_to><base_relation>parent_equipment</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>devchain_iid</inverse_name><rdb:column>INS_IID</rdb:column><rdb:join>MEAS.DEV.CHAIN_.DEVCHAIN_IID IN (?) AND MEAS.DEV.CHAIN_.INS_IID=SETUP_.INS_IID</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>sen_iid</inverse_name><rdb:join>MEASURING QUANTITY_.SEN_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>measurement_locations</name><ref_to>measurement_location</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>devchain_iid</inverse_name></relation_attribute><rdb:aid>9</rdb:aid><rdb:table>MEAS.DEV.CHAIN_</rdb:table></application_element><application_element><name>dsk</name><basetype>AoAny</basetype><application_attribute><name>dsk_iid</name><base_attribute>id</base_attribute><rdb:column>DSK_IID</rdb:column></application_attribute><application_attribute><name>created</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CREATED</rdb:column></application_attribute><application_attribute><name>creator</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CREATOR</rdb:column></application_attribute><application_attribute><name>host</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>HOST</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dsk_iid</inverse_name><rdb:join>MEASUREMENT_.DSK_IID IN (?)</rdb:join></relation_attribute><rdb:aid>10</rdb:aid><rdb:table>DISKS_</rdb:table></application_element><application_element><name>fremdmotor</name><basetype>AoUnitUnderTest</basetype><application_attribute><name>fremdmotor_iid</name><base_attribute>id</base_attribute><rdb:column>FREMDMOTOR__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>manuf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MANUF_</rdb:column></application_attribute><application_attribute><name>verbr_typ</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERBR_TYP_</rdb:column></application_attribute><application_attribute><name>zylzahl</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYLZAHL_</rdb:column></application_attribute><application_attribute><name>bauform</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BAUFORM_</rdb:column></application_attribute><application_attribute><name>besond</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BESOND_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>FREMDMOTOR_.FREMDMOTOR__IID IN (?) AND FREMDMOTOR_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>fremdmotor.exhaustsystem_iid</name><ref_to>fremdmotor.exhaustsystem</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:join>FREMDMOTOR_EXHAUSTSYSTEM.FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor.motoraufbau_iid</name><ref_to>fremdmotor.motoraufbau</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:join>FREMDMOTOR_MOTORAUFBAU.FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor.additional_iid</name><ref_to>fremdmotor.additional</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:join>FREMDMOTOR_ADDITIONAL.FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>DATA-SET_.DTS_IID=.DTS_IID AND .FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><rdb:aid>11</rdb:aid><rdb:table>FREMDMOTOR_</rdb:table></application_element><application_element><name>fremdmotor.exhaustsystem</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>fremdmotor.exhaustsystem_iid</name><base_attribute>id</base_attribute><rdb:column>FREMDMOTOR_EXHAUSTSYSTEM_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>elbow</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ELBOW_</rdb:column></application_attribute><application_attribute><name>inlet</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>INLET_</rdb:column></application_attribute><application_attribute><name>mixing</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MIXING_</rdb:column></application_attribute><application_attribute><name>cat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CAT_</rdb:column></application_attribute><application_attribute><name>dpf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>DPF_</rdb:column></application_attribute><application_attribute><name>fr_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>FR_MF_</rdb:column></application_attribute><application_attribute><name>tube2_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE2__</rdb:column></application_attribute><application_attribute><name>in_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IN_MF_</rdb:column></application_attribute><application_attribute><name>tube3_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE3__</rdb:column></application_attribute><application_attribute><name>rr_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>RR_MF_</rdb:column></application_attribute><application_attribute><name>outlet</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>OUTLET_</rdb:column></application_attribute><application_attribute><name>mount</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOUNT_</rdb:column></application_attribute><application_attribute><name>absorb</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABSORB_</rdb:column></application_attribute><application_attribute><name>suspen</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SUSPEN_</rdb:column></application_attribute><application_attribute><name>weitere</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>WEITERE_</rdb:column></application_attribute><application_attribute><name>tube1_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE1__</rdb:column></application_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>fremdmotor.exhaustsystem_iid</inverse_name><rdb:column>FREMDMOTOR__IID</rdb:column><rdb:join>FREMDMOTOR_EXHAUSTSYSTEM.FREMDMOTOR_EXHAUSTSYSTEM_IID IN (?) AND FREMDMOTOR_EXHAUSTSYSTEM.FREMDMOTOR__IID=FREMDMOTOR_.FREMDMOTOR__IID</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor.exhaustsystem_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .FREMDMOTOR_EXHAUSTSYSTEM_IID IN (?)</rdb:join></relation_attribute><rdb:aid>12</rdb:aid><rdb:table>FREMDMOTOR_EXHAUSTSYSTEM</rdb:table></application_element><application_element><name>motor</name><basetype>AoUnitUnderTest</basetype><application_attribute><name>motor_iid</name><base_attribute>id</base_attribute><rdb:column>MOTOR__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>motbegkart</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>MOTBEGKART_</rdb:column><rdb:sql>SELECT BLOB FROM MOTOR_ WHERE MOTOR__IID=?</rdb:sql></application_attribute><application_attribute><name>mtyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MTYP_</rdb:column></application_attribute><application_attribute><name>mtnr</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MTNR_</rdb:column></application_attribute><application_attribute><name>bauz</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BAUZ_</rdb:column></application_attribute><application_attribute><name>verbrenn</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERBRENN_</rdb:column></application_attribute><application_attribute><name>bauart</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BAUART_</rdb:column></application_attribute><application_attribute><name>fahrzeug</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>FAHRZEUG_</rdb:column></application_attribute><application_attribute><name>kennz</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KENNZ_</rdb:column></application_attribute><application_attribute><name>gtyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GTYP_</rdb:column></application_attribute><application_attribute><name>gnr</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GNR_</rdb:column></application_attribute><application_attribute><name>riementr</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>RIEMENTR_</rdb:column></application_attribute><application_attribute><name>generator</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GENERATOR_</rdb:column></application_attribute><application_attribute><name>kmv</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KMV_</rdb:column></application_attribute><application_attribute><name>lhp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LHP_</rdb:column></application_attribute><application_attribute><name>abcpumpe</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABCPUMPE_</rdb:column></application_attribute><application_attribute><name>aggrsonst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>AGGRSONST_</rdb:column></application_attribute><application_attribute><name>besond</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BESOND_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>motor_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>MOTOR_.MOTOR__IID IN (?) AND MOTOR_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>motor.exhaustsystem_iid</name><ref_to>motor.exhaustsystem</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:join>MOTOR_EXHAUSTSYSTEM.MOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor.motoraufbau_iid</name><ref_to>motor.motoraufbau</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:join>MOTOR_MOTORAUFBAU.MOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor.additional_iid</name><ref_to>motor.additional</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:join>MOTOR_ADDITIONAL.MOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .MOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>DATA-SET_.DTS_IID=.DTS_IID AND .MOTOR__IID IN (?)</rdb:join></relation_attribute><rdb:aid>13</rdb:aid><rdb:table>MOTOR_</rdb:table></application_element><application_element><name>motor.exhaustsystem</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>motor.exhaustsystem_iid</name><base_attribute>id</base_attribute><rdb:column>MOTOR_EXHAUSTSYSTEM_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>elbow</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ELBOW_</rdb:column></application_attribute><application_attribute><name>suspen</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SUSPEN_</rdb:column></application_attribute><application_attribute><name>inlet</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>INLET_</rdb:column></application_attribute><application_attribute><name>mixing</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MIXING_</rdb:column></application_attribute><application_attribute><name>cat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CAT_</rdb:column></application_attribute><application_attribute><name>dpf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>DPF_</rdb:column></application_attribute><application_attribute><name>tube1_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE1__</rdb:column></application_attribute><application_attribute><name>fr_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>FR_MF_</rdb:column></application_attribute><application_attribute><name>tube2_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE2__</rdb:column></application_attribute><application_attribute><name>in_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IN_MF_</rdb:column></application_attribute><application_attribute><name>tube3_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE3__</rdb:column></application_attribute><application_attribute><name>rr_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>RR_MF_</rdb:column></application_attribute><application_attribute><name>outlet</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>OUTLET_</rdb:column></application_attribute><application_attribute><name>mount</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOUNT_</rdb:column></application_attribute><application_attribute><name>absorb</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABSORB_</rdb:column></application_attribute><application_attribute><name>weitere</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>WEITERE_</rdb:column></application_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>motor.exhaustsystem_iid</inverse_name><rdb:column>MOTOR__IID</rdb:column><rdb:join>MOTOR_EXHAUSTSYSTEM.MOTOR_EXHAUSTSYSTEM_IID IN (?) AND MOTOR_EXHAUSTSYSTEM.MOTOR__IID=MOTOR_.MOTOR__IID</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor.exhaustsystem_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .MOTOR_EXHAUSTSYSTEM_IID IN (?)</rdb:join></relation_attribute><rdb:aid>14</rdb:aid><rdb:table>MOTOR_EXHAUSTSYSTEM</rdb:table></application_element><application_element><name>messtext</name><basetype>AoTestSequence</basetype><application_attribute><name>messtext_iid</name><base_attribute>id</base_attribute><rdb:column>MESSTEXT__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>aufnehmer</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>AUFNEHMER_</rdb:column><rdb:sql>SELECT BLOB FROM MESSTEXT_ WHERE MESSTEXT__IID=?</rdb:sql></application_attribute><application_attribute><name>t</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>T_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>messtext_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>MESSTEXT_.MESSTEXT__IID IN (?) AND MESSTEXT_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>messtext_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .MESSTEXT__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>messtext_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>DATA-SET_.DTS_IID=.DTS_IID AND .MESSTEXT__IID IN (?)</rdb:join></relation_attribute><rdb:aid>15</rdb:aid><rdb:table>MESSTEXT_</rdb:table></application_element><application_element><name>randbedingung</name><basetype>AoAny</basetype><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>randbedingung_iid</name><base_attribute>id</base_attribute><rdb:column>RANDBEDINGUNG__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>ftemp</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>FTEMP_</rdb:column></application_attribute><application_attribute><name>wgeschw</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>WGESCHW_</rdb:column></application_attribute><application_attribute><name>wricht</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>WRICHT_</rdb:column></application_attribute><application_attribute><name>mdat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MDAT_</rdb:column></application_attribute><application_attribute><name>mtim</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MTIM_</rdb:column></application_attribute><application_attribute><name>mort</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MORT_</rdb:column></application_attribute><application_attribute><name>prbes</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>PRBES_</rdb:column></application_attribute><application_attribute><name>ltemp</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>LTEMP_</rdb:column></application_attribute><application_attribute><name>ldruck</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>LDRUCK_</rdb:column></application_attribute><application_attribute><name>feuchte</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>FEUCHTE_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>randbedingung_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>RANDBEDINGUNG_.RANDBEDINGUNG__IID IN (?) AND RANDBEDINGUNG_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>mea-for-AnyId</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>AnyId</inverse_name><rdb:reftable/><rdb:refname>AnyId</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .RANDBEDINGUNG__IID IN (?)</rdb:join></relation_attribute><rdb:aid>16</rdb:aid><rdb:table>RANDBEDINGUNG_</rdb:table></application_element><application_element><name>messzyklus</name><basetype>AoAny</basetype><application_attribute><name>messzyklus_iid</name><base_attribute>id</base_attribute><rdb:column>MESSZYKLUS__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>zyklus</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYKLUS_</rdb:column></application_attribute><application_attribute><name>zykl_ber</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYKL_BER_</rdb:column></application_attribute><application_attribute><name>lastzust</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LASTZUST_</rdb:column></application_attribute><application_attribute><name>kueb</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KUEB_</rdb:column></application_attribute><application_attribute><name>aufnehmer</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>AUFNEHMER_</rdb:column><rdb:sql>SELECT BLOB FROM MESSZYKLUS_ WHERE MESSZYKLUS__IID=?</rdb:sql></application_attribute><application_attribute><name>gang</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GANG_</rdb:column></application_attribute><application_attribute><name>kmvkomm</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KMVKOMM_</rdb:column></application_attribute><application_attribute><name>lhp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LHP_</rdb:column></application_attribute><application_attribute><name>lhpkomm</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LHPKOMM_</rdb:column></application_attribute><application_attribute><name>lima</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LIMA_</rdb:column></application_attribute><application_attribute><name>limakomm</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LIMAKOMM_</rdb:column></application_attribute><application_attribute><name>measc</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MEASC_</rdb:column></application_attribute><application_attribute><name>kmv</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KMV_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>messzyklus_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>MESSZYKLUS_.MESSZYKLUS__IID IN (?) AND MESSZYKLUS_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>mea-for-CycId</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>CycId</inverse_name><rdb:reftable/><rdb:refname>CycId</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .MESSZYKLUS__IID IN (?)</rdb:join></relation_attribute><rdb:aid>17</rdb:aid><rdb:table>MESSZYKLUS_</rdb:table></application_element><application_element><name>mea</name><basetype>AoSubTest</basetype><application_attribute><name>postproc</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>POSTPROC</rdb:column></application_attribute><application_attribute><name>touchdate</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>TOUCHDATE</rdb:column></application_attribute><application_attribute><name>cirevision</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CIREVISION</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>mea_iid</name><base_attribute>id</base_attribute><rdb:column>MEA_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><application_attribute><name>meastime</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MEASTIME</rdb:column></application_attribute><application_attribute><name>size</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>SPACE</rdb:column></application_attribute><application_attribute><name>swrevision</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SWREVISION</rdb:column></application_attribute><application_attribute><name>prj</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>PRJ</rdb:column></application_attribute><application_attribute><name>tstser</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TSTSER</rdb:column></application_attribute><application_attribute><name>seq</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SEQ</rdb:column></application_attribute><application_attribute><name>ins</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>INS</rdb:column></application_attribute><application_attribute><name>uut</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>UUT</rdb:column></application_attribute><application_attribute><name>label</name><base_attribute>version</base_attribute><rdb:column>DSK_IID</rdb:column></application_attribute><application_attribute><name>measdate</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>MEASDATE</rdb:column></application_attribute><application_attribute><name>swstate</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SWSTATE</rdb:column></application_attribute><application_attribute><name>state</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>STATE</rdb:column></application_attribute><application_attribute><name>cidate</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CIDATE</rdb:column></application_attribute><application_attribute><name>discpath</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>DISCPATH</rdb:column></application_attribute><application_attribute><name>typ</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TYP</rdb:column></application_attribute><application_attribute><name>tstsubject</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TSTSUBJECT_</rdb:column></application_attribute><application_attribute><name>tststand</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TSTSTAND_</rdb:column></application_attribute><application_attribute><name>expert</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>EXPERT_</rdb:column></application_attribute><application_attribute><name>expertdiv</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>EXPERTDIV_</rdb:column></application_attribute><application_attribute><name>orderer</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ORDERER_</rdb:column></application_attribute><application_attribute><name>ordererdiv</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ORDERERDIV_</rdb:column></application_attribute><application_attribute><name>auftrdat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>AUFTRDAT_</rdb:column></application_attribute><application_attribute><name>ausfuehrdat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>AUSFUEHRDAT_</rdb:column></application_attribute><application_attribute><name>kontierung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KONTIERUNG_</rdb:column></application_attribute><application_attribute><name>auftrnr</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>AUFTRNR_</rdb:column></application_attribute><relation_attribute><name>tstser_iid</name><ref_to>tstser</ref_to><base_relation>parent_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>mea_iid</inverse_name><rdb:column>TSTSER_IID</rdb:column><rdb:join>MEASUREMENT_.MEA_IID IN (?) AND MEASUREMENT_.TSTSER_IID=TEST_.TSTSER_IID</rdb:join></relation_attribute><relation_attribute><name>ins_iid</name><ref_to>ins</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>mea_iid</inverse_name><rdb:column>INS_IID</rdb:column><rdb:join>MEASUREMENT_.MEA_IID IN (?) AND MEASUREMENT_.INS_IID=SETUP_.INS_IID</rdb:join></relation_attribute><relation_attribute><name>dsk_iid</name><ref_to>dsk</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>mea_iid</inverse_name><rdb:column>DSK_IID</rdb:column><rdb:join>MEASUREMENT_.MEA_IID IN (?) AND MEASUREMENT_.DSK_IID=DISKS_.DSK_IID</rdb:join></relation_attribute><application_attribute><name>attachments</name><datatype>DS_EXTERNALREFERENCE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>ATTACHMENTS</rdb:column><rdb:value>'NULL'</rdb:value><rdb:sql>N/A</rdb:sql></application_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:join>DATA-SET_.MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>FREMDMOTOR_.FREMDMOTOR__IID=.FREMDMOTOR__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor.exhaustsystem_iid</name><ref_to>fremdmotor.exhaustsystem</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>FREMDMOTOR_EXHAUSTSYSTEM.FREMDMOTOR_EXHAUSTSYSTEM_IID=.FREMDMOTOR_EXHAUSTSYSTEM_IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MOTOR_.MOTOR__IID=.MOTOR__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor.exhaustsystem_iid</name><ref_to>motor.exhaustsystem</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MOTOR_EXHAUSTSYSTEM.MOTOR_EXHAUSTSYSTEM_IID=.MOTOR_EXHAUSTSYSTEM_IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>messtext_iid</name><ref_to>messtext</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MESSTEXT_.MESSTEXT__IID=.MESSTEXT__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>AnyId</name><ref_to>randbedingung</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea-for-AnyId</inverse_name><rdb:reftable/><rdb:refname>AnyId</rdb:refname><rdb:join>RANDBEDINGUNG_.RANDBEDINGUNG__IID=.RANDBEDINGUNG__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>CycId</name><ref_to>messzyklus</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea-for-CycId</inverse_name><rdb:reftable/><rdb:refname>CycId</rdb:refname><rdb:join>MESSZYKLUS_.MESSZYKLUS__IID=.MESSZYKLUS__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><rdb:aid>18</rdb:aid><rdb:table>MEASUREMENT_</rdb:table></application_element><application_element><name>pas</name><basetype>AoParameterSet</basetype><application_attribute><name>pas_iid</name><base_attribute>id</base_attribute><rdb:column>PAS_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>description</name><base_attribute>description</base_attribute><length>4000</length><rdb:column>DESCR</rdb:column></application_attribute><relation_attribute><name>par_iid</name><ref_to>par</ref_to><base_relation>parameters</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>pas_iid</inverse_name><rdb:join>PARAMETER_.PAS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>pas_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASURING QUANTITY_.MEQ_IID=.MEQ_IID AND .PAS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>pas_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>DATA-SET_.DTS_IID=.DTS_IID AND .PAS_IID IN (?)</rdb:join></relation_attribute><rdb:aid>19</rdb:aid><rdb:table>PARAMETER-SET_</rdb:table></application_element><application_element><name>dts</name><basetype>AoMeasurement</basetype><application_attribute><name>dts_iid</name><base_attribute>id</base_attribute><rdb:column>DTS_IID</rdb:column></application_attribute><application_attribute><name>measbegin</name><base_attribute>measurement_begin</base_attribute><rdb:column>MEASBEGIN</rdb:column></application_attribute><application_attribute><name>measend</name><base_attribute>measurement_end</base_attribute><rdb:column>MEASEND</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><base_relation>test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>dts_iid</inverse_name><rdb:column>MEA_IID</rdb:column><rdb:join>DATA-SET_.DTS_IID IN (?) AND DATA-SET_.MEA_IID=MEASUREMENT_.MEA_IID</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><base_relation>measurement_quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:join>MEASURING QUANTITY_.DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>pas_iid</name><ref_to>pas</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>PARAMETER-SET_.PAS_IID=.PAS_IID AND .DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>units_under_test</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>FREMDMOTOR_.FREMDMOTOR__IID=.FREMDMOTOR__IID AND .DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>units_under_test</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MOTOR_.MOTOR__IID=.MOTOR__IID AND .DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>messtext_iid</name><ref_to>messtext</ref_to><base_relation>sequences</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MESSTEXT_.MESSTEXT__IID=.MESSTEXT__IID AND .DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>sm_iid</name><ref_to>sm</ref_to><base_relation>submatrices</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name></relation_attribute><relation_attribute><name>geometry</name><ref_to>geometry</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>measurements_on_geometry</inverse_name></relation_attribute><rdb:aid>20</rdb:aid><rdb:table>DATA-SET_</rdb:table></application_element><application_element><name>quantity</name><basetype>AoQuantity</basetype><application_attribute><name>quantity_iid</name><base_attribute>id</base_attribute><rdb:column>QUANTITY_IID</rdb:column></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><application_attribute><name>def_name</name><base_attribute>default_mq_name</base_attribute><length>4000</length><rdb:column>DEF_NAME</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>default_unit</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>quantity_iid</inverse_name><rdb:column>UNT_IID</rdb:column><rdb:join>QUANTITY_.QUANTITY_IID IN (?) AND QUANTITY_.UNT_IID=UNIT_.UNT_IID</rdb:join></relation_attribute><relation_attribute><name>pred_iid</name><ref_to>quantity</ref_to><base_relation>predecessor</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>suc_iid</inverse_name><rdb:column>PRED_IID</rdb:column><rdb:join>QUANTITY_.QUANTITY_IID=(SELECT QUANTITY_.PRED_IID FROM QUANTITY_ WHERE QUANTITY_.QUANTITY_IID IN (?))</rdb:join></relation_attribute><relation_attribute><name>suc_iid</name><ref_to>quantity</ref_to><base_relation>successors</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>pred_iid</inverse_name><rdb:join>QUANTITY_.PRED_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><base_relation>measurement_quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>quantity_iid</inverse_name><rdb:join>MEASURING QUANTITY_.QUANTITY_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>grq_iid</name><ref_to>grq</ref_to><base_relation>groups</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>quantity_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>QUANTITYSYSTEM_.GRQ_IID=.GRQ_IID AND .QUANTITY_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq-for-non_reference_channel_quantity</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>non_reference_channel_quantity</inverse_name><rdb:reftable/><rdb:refname>non_reference_channel_quantity</rdb:refname><rdb:join>MEASURING QUANTITY_.MEQ_IID=.MEQ_IID AND .QUANTITY_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq-for-reference_channel_quantity</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>reference_channel_quantity</inverse_name><rdb:reftable/><rdb:refname>reference_channel_quantity</rdb:refname><rdb:join>MEASURING QUANTITY_.MEQ_IID=.MEQ_IID AND .QUANTITY_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>used_in_measurement_locations</name><ref_to>measurement_location</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>channel_quantity</inverse_name></relation_attribute><rdb:aid>21</rdb:aid><rdb:table>QUANTITY_</rdb:table></application_element><application_element><name>meq</name><basetype>AoMeasurementQuantity</basetype><application_attribute><name>meq_iid</name><base_attribute>id</base_attribute><rdb:column>MEQ_IID</rdb:column></application_attribute><application_attribute><name>aodt</name><base_attribute>datatype</base_attribute><rdb:column>AODT</rdb:column></application_attribute><application_attribute><name>non_reference_channel_name</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>NONREFCHN</rdb:column></application_attribute><application_attribute><name>reference_channel_name</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>REFCHN</rdb:column></application_attribute><application_attribute><name>min_val</name><base_attribute>minimum</base_attribute><rdb:column>MIN_VAL</rdb:column></application_attribute><application_attribute><name>max_val</name><base_attribute>maximum</base_attribute><rdb:column>MAX_VAL</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>description</name><base_attribute>description</base_attribute><length>4000</length><rdb:column>DESCR</rdb:column></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>meq_iid</inverse_name><rdb:column>DTS_IID</rdb:column><rdb:join>MEASURING QUANTITY_.MEQ_IID IN (?) AND MEASURING QUANTITY_.DTS_IID=DATA-SET_.DTS_IID</rdb:join></relation_attribute><relation_attribute><name>quantity_iid</name><ref_to>quantity</ref_to><base_relation>quantity</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>meq_iid</inverse_name><rdb:column>QUANTITY_IID</rdb:column><rdb:join>MEASURING QUANTITY_.MEQ_IID IN (?) AND MEASURING QUANTITY_.QUANTITY_IID=QUANTITY_.QUANTITY_IID</rdb:join></relation_attribute><relation_attribute><name>sen_iid</name><ref_to>devchain</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>meq_iid</inverse_name><rdb:column>SEN_IID</rdb:column><rdb:join>MEASURING QUANTITY_.MEQ_IID IN (?) AND MEASURING QUANTITY_.SEN_IID=MEAS.DEV.CHAIN_.DEVCHAIN_IID</rdb:join></relation_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>unit</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>meq_iid</inverse_name><rdb:column>UNT_IID</rdb:column><rdb:join>MEASURING QUANTITY_.MEQ_IID IN (?) AND MEASURING QUANTITY_.UNT_IID=UNIT_.UNT_IID</rdb:join></relation_attribute><relation_attribute><name>non_reference_channel_quantity</name><ref_to>quantity</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>meq-for-non_reference_channel_quantity</inverse_name><rdb:reftable/><rdb:refname>non_reference_channel_quantity</rdb:refname><rdb:join>QUANTITY_.QUANTITY_IID=.QUANTITY_IID AND .MEQ_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>reference_channel_quantity</name><ref_to>quantity</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>meq-for-reference_channel_quantity</inverse_name><rdb:reftable/><rdb:refname>reference_channel_quantity</rdb:refname><rdb:join>QUANTITY_.QUANTITY_IID=.QUANTITY_IID AND .MEQ_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>pas_iid</name><ref_to>pas</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>meq_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>PARAMETER-SET_.PAS_IID=.PAS_IID AND .MEQ_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>lc_iid</name><ref_to>lc</ref_to><base_relation>local_columns</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>meq_iid</inverse_name></relation_attribute><relation_attribute><name>non_reference_locations</name><ref_to>measurement_location</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>in_reference_location</inverse_name></relation_attribute><relation_attribute><name>reference_locations</name><ref_to>measurement_location</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>in_non_reference_location</inverse_name></relation_attribute><rdb:aid>22</rdb:aid><rdb:table>MEASURING QUANTITY_</rdb:table></application_element><application_element><name>par</name><basetype>AoParameter</basetype><application_attribute><name>par_iid</name><base_attribute>id</base_attribute><rdb:column>PAR_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>pval</name><base_attribute>pvalue</base_attribute><length>4000</length><rdb:column>PVAL</rdb:column></application_attribute><application_attribute><name>ptyp</name><base_attribute>parameter_datatype</base_attribute><rdb:column>PTYP</rdb:column></application_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>unit</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>par_iid</inverse_name><rdb:column>UNT_IID</rdb:column><rdb:join>PARAMETER_.PAR_IID IN (?) AND PARAMETER_.UNT_IID=UNIT_.UNT_IID</rdb:join></relation_attribute><relation_attribute><name>pas_iid</name><ref_to>pas</ref_to><base_relation>parameter_set</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>par_iid</inverse_name><rdb:column>PAS_IID</rdb:column><rdb:join>PARAMETER_.PAR_IID IN (?) AND PARAMETER_.PAS_IID=PARAMETER-SET_.PAS_IID</rdb:join></relation_attribute><rdb:aid>26</rdb:aid><rdb:table>PARAMETER_</rdb:table></application_element><application_element><name>fremdmotor.motoraufbau</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>fremdmotor.motoraufbau_iid</name><base_attribute>id</base_attribute><rdb:column>FREMDMOTOR_MOTORAUFBAU_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>kurbelwelle</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KURBELWELLE_</rdb:column></application_attribute><application_attribute><name>kolben</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KOLBEN_</rdb:column></application_attribute><application_attribute><name>ladeluftkueh</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LADELUFTKUEH_</rdb:column></application_attribute><application_attribute><name>ladeluftleit</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LADELUFTLEIT_</rdb:column></application_attribute><application_attribute><name>absblenden</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABSBLENDEN_</rdb:column></application_attribute><application_attribute><name>kraftstoff</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KRAFTSTOFF_</rdb:column></application_attribute><application_attribute><name>klopfregelung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KLOPFREGELUNG_</rdb:column></application_attribute><application_attribute><name>gelenkwelle</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GELENKWELLE_</rdb:column></application_attribute><application_attribute><name>sonst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SONST_</rdb:column></application_attribute><application_attribute><name>swstand</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SWSTAND_</rdb:column></application_attribute><application_attribute><name>abdeckung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABDECKUNG_</rdb:column></application_attribute><application_attribute><name>rohlufthutzen</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ROHLUFTHUTZEN_</rdb:column></application_attribute><application_attribute><name>luftfilter</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LUFTFILTER_</rdb:column></application_attribute><application_attribute><name>oelwanne</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>OELWANNE_</rdb:column></application_attribute><application_attribute><name>motorlager</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOTORLAGER_</rdb:column></application_attribute><application_attribute><name>motortraeger</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOTORTRAEGER_</rdb:column></application_attribute><application_attribute><name>getriebelager</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GETRIEBELAGER_</rdb:column></application_attribute><application_attribute><name>torsdaempf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TORSDAEMPF_</rdb:column></application_attribute><application_attribute><name>kurbelgeh</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KURBELGEH_</rdb:column></application_attribute><application_attribute><name>zylkopf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYLKOPF_</rdb:column></application_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>fremdmotor.motoraufbau_iid</inverse_name><rdb:column>FREMDMOTOR__IID</rdb:column><rdb:join>FREMDMOTOR_MOTORAUFBAU.FREMDMOTOR_MOTORAUFBAU_IID IN (?) AND FREMDMOTOR_MOTORAUFBAU.FREMDMOTOR__IID=FREMDMOTOR_.FREMDMOTOR__IID</rdb:join></relation_attribute><rdb:aid>27</rdb:aid><rdb:table>FREMDMOTOR_MOTORAUFBAU</rdb:table></application_element><application_element><name>fremdmotor.additional</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>fremdmotor.additional_iid</name><base_attribute>id</base_attribute><rdb:column>FREMDMOTOR_ADDITIONAL_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>add</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ADD_</rdb:column></application_attribute><application_attribute><name>auftrag</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>AUFTRAG_</rdb:column><rdb:sql>SELECT BLOB FROM FREMDMOTOR_ADDITIONAL WHERE FREMDMOTOR_ADDITIONAL_IID=?</rdb:sql></application_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>fremdmotor.additional_iid</inverse_name><rdb:column>FREMDMOTOR__IID</rdb:column><rdb:join>FREMDMOTOR_ADDITIONAL.FREMDMOTOR_ADDITIONAL_IID IN (?) AND FREMDMOTOR_ADDITIONAL.FREMDMOTOR__IID=FREMDMOTOR_.FREMDMOTOR__IID</rdb:join></relation_attribute><rdb:aid>28</rdb:aid><rdb:table>FREMDMOTOR_ADDITIONAL</rdb:table></application_element><application_element><name>motor.motoraufbau</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>motor.motoraufbau_iid</name><base_attribute>id</base_attribute><rdb:column>MOTOR_MOTORAUFBAU_IID</rdb:column></application_attribute><application_attribute><name>swstand</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SWSTAND_</rdb:column></application_attribute><application_attribute><name>abdeckung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABDECKUNG_</rdb:column></application_attribute><application_attribute><name>rohlufthutzen</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ROHLUFTHUTZEN_</rdb:column></application_attribute><application_attribute><name>luftfilter</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LUFTFILTER_</rdb:column></application_attribute><application_attribute><name>oelwanne</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>OELWANNE_</rdb:column></application_attribute><application_attribute><name>motorlager</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOTORLAGER_</rdb:column></application_attribute><application_attribute><name>motortraeger</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOTORTRAEGER_</rdb:column></application_attribute><application_attribute><name>getriebelager</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GETRIEBELAGER_</rdb:column></application_attribute><application_attribute><name>torsdaempf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TORSDAEMPF_</rdb:column></application_attribute><application_attribute><name>zylkopf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYLKOPF_</rdb:column></application_attribute><application_attribute><name>kurbelgeh</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KURBELGEH_</rdb:column></application_attribute><application_attribute><name>kurbelwelle</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KURBELWELLE_</rdb:column></application_attribute><application_attribute><name>kolben</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KOLBEN_</rdb:column></application_attribute><application_attribute><name>ladeluftkueh</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LADELUFTKUEH_</rdb:column></application_attribute><application_attribute><name>ladeluftleit</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LADELUFTLEIT_</rdb:column></application_attribute><application_attribute><name>absblenden</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABSBLENDEN_</rdb:column></application_attribute><application_attribute><name>kraftstoff</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KRAFTSTOFF_</rdb:column></application_attribute><application_attribute><name>klopfregelung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KLOPFREGELUNG_</rdb:column></application_attribute><application_attribute><name>sonst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SONST_</rdb:column></application_attribute><application_attribute><name>gelenkwelle</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GELENKWELLE_</rdb:column></application_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>motor.motoraufbau_iid</inverse_name><rdb:column>MOTOR__IID</rdb:column><rdb:join>MOTOR_MOTORAUFBAU.MOTOR_MOTORAUFBAU_IID IN (?) AND MOTOR_MOTORAUFBAU.MOTOR__IID=MOTOR_.MOTOR__IID</rdb:join></relation_attribute><rdb:aid>29</rdb:aid><rdb:table>MOTOR_MOTORAUFBAU</rdb:table></application_element><application_element><name>motor.additional</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>motor.additional_iid</name><base_attribute>id</base_attribute><rdb:column>MOTOR_ADDITIONAL_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>add</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ADD_</rdb:column></application_attribute><application_attribute><name>auftrag</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>AUFTRAG_</rdb:column><rdb:sql>SELECT BLOB FROM MOTOR_ADDITIONAL WHERE MOTOR_ADDITIONAL_IID=?</rdb:sql></application_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>motor.additional_iid</inverse_name><rdb:column>MOTOR__IID</rdb:column><rdb:join>MOTOR_ADDITIONAL.MOTOR_ADDITIONAL_IID IN (?) AND MOTOR_ADDITIONAL.MOTOR__IID=MOTOR_.MOTOR__IID</rdb:join></relation_attribute><rdb:aid>30</rdb:aid><rdb:table>MOTOR_ADDITIONAL</rdb:table></application_element><application_element><name>sm</name><basetype>AoSubmatrix</basetype><application_attribute><name>sm_iid</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute></application_attribute><application_attribute><name>number_of_rows</name><base_attribute>number_of_rows</base_attribute></application_attribute><relation_attribute><name>lc_iid</name><ref_to>lc</ref_to><base_relation>local_columns</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>sm_iid</inverse_name></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>sm_iid</inverse_name></relation_attribute><relation_attribute><name>x-axis-for-y-axis</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>y-axis-for-x-axis</inverse_name></relation_attribute><relation_attribute><name>z-axis-for-y-axis</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>y-axis-for-z-axis</inverse_name></relation_attribute><relation_attribute><name>y-axis-for-x-axis</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>x-axis-for-y-axis</inverse_name></relation_attribute><relation_attribute><name>y-axis-for-z-axis</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>z-axis-for-y-axis</inverse_name></relation_attribute><relation_attribute><name>geometry</name><ref_to>geometry</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>geometry_shapes</inverse_name></relation_attribute></application_element><application_element><name>lc</name><basetype>AoLocalColumn</basetype><application_attribute><name>lc_iid</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>independent</name><base_attribute>independent</base_attribute></application_attribute><application_attribute><name>values</name><base_attribute>values</base_attribute></application_attribute><application_attribute><name>sequence_representation</name><base_attribute>sequence_representation</base_attribute></application_attribute><application_attribute><name>generation_parameters</name><base_attribute>generation_parameters</base_attribute></application_attribute><application_attribute><name>raw_datatype</name><base_attribute>raw_datatype</base_attribute></application_attribute><application_attribute><name>axistype</name><datatype>DT_ENUM</datatype><enumeration_type>axistype</enumeration_type></application_attribute><application_attribute><name>global_flag</name><base_attribute>global_flag</base_attribute></application_attribute><application_attribute><name>flags</name><base_attribute>flags</base_attribute></application_attribute><relation_attribute><name>ec_iid</name><ref_to>ec</ref_to><base_relation>external_component</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>lc_iid</inverse_name></relation_attribute><relation_attribute><name>sm_iid</name><ref_to>sm</ref_to><base_relation>submatrix</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>lc_iid</inverse_name></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><base_relation>measurement_quantity</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>lc_iid</inverse_name></relation_attribute></application_element><application_element><name>ec</name><basetype>AoExternalComponent</basetype><application_attribute><name>ec_iid</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>description</name><base_attribute>description</base_attribute></application_attribute><application_attribute><name>ordinal_number</name><base_attribute>ordinal_number</base_attribute></application_attribute><application_attribute><name>component_length</name><base_attribute>component_length</base_attribute></application_attribute><application_attribute><name>filename_url</name><base_attribute>filename_url</base_attribute></application_attribute><application_attribute><name>value_type</name><base_attribute>value_type</base_attribute></application_attribute><application_attribute><name>start_offset</name><base_attribute>start_offset</base_attribute></application_attribute><application_attribute><name>block_size</name><base_attribute>block_size</base_attribute></application_attribute><application_attribute><name>valuesperblock</name><base_attribute>valuesperblock</base_attribute></application_attribute><application_attribute><name>value_offset</name><base_attribute>value_offset</base_attribute></application_attribute><relation_attribute><name>lc_iid</name><ref_to>lc</ref_to><base_relation>local_column</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>ec_iid</inverse_name></relation_attribute></application_element><application_element><name>coordinate_system</name><basetype>AoAny</basetype><application_attribute><name>id</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>name</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute></application_attribute><application_attribute><name>coordinate_system_type</name><datatype>DT_ENUM</datatype><enumeration_type>coordinate_system_types</enumeration_type></application_attribute><application_attribute><name>origin_1</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>origin_2</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>origin_3</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>x_axis_1</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>x_axis_2</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>x_axis_3</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>xz_plane_1</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>xz_plane_2</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>xz_plane_3</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><relation_attribute><name>reference_system</name><ref_to>coordinate_system</ref_to><base_relation>parent</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>locale_systems</inverse_name></relation_attribute><relation_attribute><name>locale_systems</name><ref_to>coordinate_system</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>reference_system</inverse_name></relation_attribute><relation_attribute><name>used_in_geometries</name><ref_to>geometry</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>coordinate_system</inverse_name></relation_attribute><relation_attribute><name>used_in_measurement_locations</name><ref_to>measurement_location</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>coordinate_system</inverse_name></relation_attribute></application_element><application_element><name>geometry</name><basetype>AoAny</basetype><application_attribute><name>id</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>name</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute></application_attribute><application_attribute><name>geometry_id</name><datatype>DT_LONG</datatype></application_attribute><relation_attribute><name>coordinate_system</name><ref_to>coordinate_system</ref_to><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>used_in_geometries</inverse_name></relation_attribute><relation_attribute><name>sub_geometries</name><ref_to>geometry</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>parent_geometry</inverse_name></relation_attribute><relation_attribute><name>parent_geometry</name><ref_to>geometry</ref_to><base_relation>parent</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>sub_geometries</inverse_name></relation_attribute><relation_attribute><name>measurements_on_geometry</name><ref_to>dts</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>geometry</inverse_name></relation_attribute><relation_attribute><name>geometry_shapes</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>geometry</inverse_name></relation_attribute></application_element><application_element><name>measurement_location</name><basetype>AoTestEquipmentPart</basetype><application_attribute><name>id</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>name</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>location_shape_id</name><datatype>DT_LONG</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>location_geometry_id</name><datatype>DT_LONG</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>location_mode</name><datatype>DT_ENUM</datatype><enumeration_type>location_modes</enumeration_type><obligatory>true</obligatory></application_attribute><application_attribute><name>channel_name</name><datatype>DT_STRING</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>channel_description</name><datatype>DT_STRING</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>u</name><datatype>DT_FLOAT</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>v</name><datatype>DT_FLOAT</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>w</name><datatype>DT_FLOAT</datatype><obligatory>true</obligatory></application_attribute><relation_attribute><name>coordinate_system</name><ref_to>coordinate_system</ref_to><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>used_in_measurement_locations</inverse_name></relation_attribute><relation_attribute><name>channel_quantity</name><ref_to>quantity</ref_to><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>used_in_measurement_locations</inverse_name></relation_attribute><relation_attribute><name>in_reference_location</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>reference_locations</inverse_name></relation_attribute><relation_attribute><name>in_non_reference_location</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>non_reference_locations</inverse_name></relation_attribute><relation_attribute><name>devchain_iid</name><ref_to>devchain</ref_to><base_relation>parent_equipment_part</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>measurement_locations</inverse_name></relation_attribute></application_element></application_model></atfx_file>
+
diff --git a/org.eclipse.mdm.filerelease/src/main/configuration/EXP/model.atfx.technical b/org.eclipse.mdm.filerelease/src/main/configuration/EXP/model.atfx.technical
new file mode 100644
index 0000000..19c9e77
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/configuration/EXP/model.atfx.technical
@@ -0,0 +1,17 @@
+<!-- ********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ******************************************************************************** -->
+<?xml version="1.0" encoding="utf-8"?>
+
+<atfx_file xmlns="http://www.asam.net/ODS/5.1/Schema" version="atfx_file: V1.1.0" xmlns:ext="urn:schemas-microsoft-com:xslt" xmlns:rdb="http://www.muellerbbm-vas.de/ods/rdb" xmlns:utils="java:vas.xml.Util"><base_model_version>asam29</base_model_version><application_model><application_enumeration><name>axistype</name><item><name>Xaxis</name><value>0</value></item><item><name>Yaxis</name><value>1</value></item><item><name>Both</name><value>2</value></item></application_enumeration><application_enumeration><name>coordinate_system_types</name><item><name>Cartesian</name><value>0</value></item><item><name>Polar</name><value>1</value></item><item><name>Cylindric</name><value>2</value></item></application_enumeration><application_enumeration><name>location_modes</name><item><name>Fixed</name><value>0</value></item><item><name>Varying</name><value>1</value></item></application_enumeration><application_element><name>env</name><basetype>AoEnvironment</basetype><application_attribute><name>env_iid</name><base_attribute>id</base_attribute><rdb:column>ENV_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>skill</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>SKILL</rdb:column></application_attribute><application_attribute><name>id</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>ID</rdb:column></application_attribute><application_attribute><name>touched</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>TOUCHED</rdb:column></application_attribute><application_attribute><name>description</name><base_attribute>description</base_attribute><length>4000</length><rdb:column>DESCR</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>VERSION</rdb:column></application_attribute><relation_attribute><name>prj_iid</name><ref_to>prj</ref_to><base_relation>tests</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>PROJECT__.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>ins_iid</name><ref_to>ins</ref_to><base_relation>equipments</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>SETUP_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>uuts</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>FREMDMOTOR_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>uuts</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>MOTOR_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>messtext_iid</name><ref_to>messtext</ref_to><base_relation>sequences</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>MESSTEXT_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>randbedingung_iid</name><ref_to>randbedingung</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>RANDBEDINGUNG_.ENV_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>messzyklus_iid</name><ref_to>messzyklus</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>env_iid</inverse_name><rdb:join>MESSZYKLUS_.ENV_IID IN (?)</rdb:join></relation_attribute><rdb:aid>1</rdb:aid><rdb:table>ENVIRONMENT_</rdb:table></application_element><application_element><name>dim</name><basetype>AoPhysicalDimension</basetype><application_attribute><name>dim_iid</name><base_attribute>id</base_attribute><rdb:column>DIM_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>meter_exp</name><base_attribute>length_exp</base_attribute><rdb:column>LEN</rdb:column></application_attribute><application_attribute><name>kilogramm_exp</name><base_attribute>mass_exp</base_attribute><rdb:column>MASS</rdb:column></application_attribute><application_attribute><name>second_exp</name><base_attribute>time_exp</base_attribute><rdb:column>TIM</rdb:column></application_attribute><application_attribute><name>ampere_exp</name><base_attribute>current_exp</base_attribute><rdb:column>CUR</rdb:column></application_attribute><application_attribute><name>kelvin_exp</name><base_attribute>temperature_exp</base_attribute><rdb:column>T</rdb:column></application_attribute><application_attribute><name>mol_exp</name><base_attribute>molar_amount_exp</base_attribute><rdb:column>PART</rdb:column></application_attribute><application_attribute><name>candela_exp</name><base_attribute>luminous_intensity_exp</base_attribute><rdb:column>LUM</rdb:column></application_attribute><application_attribute><name>angle</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>ANGLE</rdb:column></application_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>units</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dim_iid</inverse_name><rdb:join>UNIT_.DIM_IID IN (?)</rdb:join></relation_attribute><rdb:aid>2</rdb:aid><rdb:table>DIMENSION_</rdb:table></application_element><application_element><name>gru</name><basetype>AoUnitGroup</basetype><application_attribute><name>gru_iid</name><base_attribute>id</base_attribute><rdb:column>GRU_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>units</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>gru_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>UNIT_.UNT_IID=.UNT_IID AND .GRU_IID IN (?)</rdb:join></relation_attribute><rdb:aid>3</rdb:aid><rdb:table>UNITSYSTEM_</rdb:table></application_element><application_element><name>unt</name><basetype>AoUnit</basetype><application_attribute><name>unt_iid</name><base_attribute>id</base_attribute><rdb:column>UNT_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>offset</name><base_attribute>offset</base_attribute><rdb:column>OFFSET</rdb:column></application_attribute><application_attribute><name>factor</name><base_attribute>factor</base_attribute><rdb:column>FACTOR</rdb:column></application_attribute><application_attribute><name>dB</name><datatype>DT_SHORT</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>DBFLG</rdb:column></application_attribute><application_attribute><name>dB_reference_factor</name><datatype>DT_FLOAT</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>DBREF</rdb:column></application_attribute><relation_attribute><name>dim_iid</name><ref_to>dim</ref_to><base_relation>phys_dimension</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>unt_iid</inverse_name><rdb:column>DIM_IID</rdb:column><rdb:join>UNIT_.UNT_IID IN (?) AND UNIT_.DIM_IID=DIMENSION_.DIM_IID</rdb:join></relation_attribute><relation_attribute><name>quantity_iid</name><ref_to>quantity</ref_to><base_relation>quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>unt_iid</inverse_name><rdb:join>QUANTITY_.UNT_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><base_relation>measurement_quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>unt_iid</inverse_name><rdb:join>MEASURING QUANTITY_.UNT_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>par_iid</name><ref_to>par</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>unt_iid</inverse_name><rdb:join>PARAMETER_.UNT_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>gru_iid</name><ref_to>gru</ref_to><base_relation>groups</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>unt_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>UNITSYSTEM_.GRU_IID=.GRU_IID AND .UNT_IID IN (?)</rdb:join></relation_attribute><rdb:aid>4</rdb:aid><rdb:table>UNIT_</rdb:table></application_element><application_element><name>grq</name><basetype>AoQuantityGroup</basetype><application_attribute><name>grq_iid</name><base_attribute>id</base_attribute><rdb:column>GRQ_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>quantity_iid</name><ref_to>quantity</ref_to><base_relation>quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>grq_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>QUANTITY_.QUANTITY_IID=.QUANTITY_IID AND .GRQ_IID IN (?)</rdb:join></relation_attribute><rdb:aid>5</rdb:aid><rdb:table>QUANTITYSYSTEM_</rdb:table></application_element><application_element><name>prj</name><basetype>AoTest</basetype><application_attribute><name>prj_iid</name><base_attribute>id</base_attribute><rdb:column>PRJ_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>prj_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>PROJECT__.PRJ_IID IN (?) AND PROJECT__.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>tstser_iid</name><ref_to>tstser</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>prj_iid</inverse_name><rdb:join>TEST_.PRJ_IID IN (?)</rdb:join></relation_attribute><rdb:aid>6</rdb:aid><rdb:table>PROJECT__</rdb:table></application_element><application_element><name>tstser</name><basetype>AoSubTest</basetype><application_attribute><name>tstser_iid</name><base_attribute>id</base_attribute><rdb:column>TSTSER_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>mime_type</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><relation_attribute><name>prj_iid</name><ref_to>prj</ref_to><base_relation>parent_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>tstser_iid</inverse_name><rdb:column>PRJ_IID</rdb:column><rdb:join>TEST_.TSTSER_IID IN (?) AND TEST_.PRJ_IID=PROJECT__.PRJ_IID</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>tstser_iid</inverse_name><rdb:join>MEASUREMENT_.TSTSER_IID IN (?)</rdb:join></relation_attribute><rdb:aid>7</rdb:aid><rdb:table>TEST_</rdb:table></application_element><application_element><name>ins</name><basetype>AoTestEquipment</basetype><application_attribute><name>ins_iid</name><base_attribute>id</base_attribute><rdb:column>INS_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>setting</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>SETTING</rdb:column><rdb:sql>SELECT BLOB FROM SETUP_ WHERE INS_IID=?</rdb:sql></application_attribute><application_attribute><name>ramp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>RAMP</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>ins_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>SETUP_.INS_IID IN (?) AND SETUP_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>devchain_iid</name><ref_to>devchain</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>ins_iid</inverse_name><rdb:join>MEAS.DEV.CHAIN_.INS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>ins_iid</inverse_name><rdb:join>MEASUREMENT_.INS_IID IN (?)</rdb:join></relation_attribute><rdb:aid>8</rdb:aid><rdb:table>SETUP_</rdb:table></application_element><application_element><name>devchain</name><basetype>AoTestEquipmentPart</basetype><application_attribute><name>devofs</name><datatype>DT_FLOAT</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>DEVOFS</rdb:column></application_attribute><application_attribute><name>devchain_iid</name><base_attribute>id</base_attribute><rdb:column>DEVCHAIN_IID</rdb:column></application_attribute><application_attribute><name>smod1inp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1INP</rdb:column></application_attribute><application_attribute><name>smod2typ</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2TYP</rdb:column></application_attribute><application_attribute><name>smod2sno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2SNO</rdb:column></application_attribute><application_attribute><name>smod2pro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2PRO</rdb:column></application_attribute><application_attribute><name>smod2st</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2ST</rdb:column></application_attribute><application_attribute><name>smod2inp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD2INP</rdb:column></application_attribute><application_attribute><name>imodinp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODINP</rdb:column></application_attribute><application_attribute><name>dev</name><datatype>DT_FLOAT</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>DEV</rdb:column></application_attribute><application_attribute><name>ctrltyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CTRLTYP</rdb:column></application_attribute><application_attribute><name>ctrlsno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CTRLSNO</rdb:column></application_attribute><application_attribute><name>ctrlpro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CTRLPRO</rdb:column></application_attribute><application_attribute><name>ctrlst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CTRLST</rdb:column></application_attribute><application_attribute><name>modtyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MODTYP</rdb:column></application_attribute><application_attribute><name>modsno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MODSNO</rdb:column></application_attribute><application_attribute><name>modpro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MODPRO</rdb:column></application_attribute><application_attribute><name>modst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MODST</rdb:column></application_attribute><application_attribute><name>imodtyp</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>IMODTYP</rdb:column></application_attribute><application_attribute><name>imodsno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODSNO</rdb:column></application_attribute><application_attribute><name>imodpro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODPRO</rdb:column></application_attribute><application_attribute><name>imodst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODST</rdb:column></application_attribute><application_attribute><name>imodswreva</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODSWREVA</rdb:column></application_attribute><application_attribute><name>imodswrevb</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IMODSWREVB</rdb:column></application_attribute><application_attribute><name>smod1typ</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1TYP</rdb:column></application_attribute><application_attribute><name>smod1sno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1SNO</rdb:column></application_attribute><application_attribute><name>smod1pro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1PRO</rdb:column></application_attribute><application_attribute><name>smod1st</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SMOD1ST</rdb:column></application_attribute><application_attribute><name>senstyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SENSTYP</rdb:column></application_attribute><application_attribute><name>senssno</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SENSSNO</rdb:column></application_attribute><application_attribute><name>senspro</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SENSPRO</rdb:column></application_attribute><application_attribute><name>sensst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SENSST</rdb:column></application_attribute><relation_attribute><name>ins_iid</name><ref_to>ins</ref_to><base_relation>parent_equipment</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>devchain_iid</inverse_name><rdb:column>INS_IID</rdb:column><rdb:join>MEAS.DEV.CHAIN_.DEVCHAIN_IID IN (?) AND MEAS.DEV.CHAIN_.INS_IID=SETUP_.INS_IID</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>sen_iid</inverse_name><rdb:join>MEASURING QUANTITY_.SEN_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>measurement_locations</name><ref_to>measurement_location</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>devchain_iid</inverse_name></relation_attribute><rdb:aid>9</rdb:aid><rdb:table>MEAS.DEV.CHAIN_</rdb:table></application_element><application_element><name>dsk</name><basetype>AoAny</basetype><application_attribute><name>dsk_iid</name><base_attribute>id</base_attribute><rdb:column>DSK_IID</rdb:column></application_attribute><application_attribute><name>created</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CREATED</rdb:column></application_attribute><application_attribute><name>creator</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CREATOR</rdb:column></application_attribute><application_attribute><name>host</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>HOST</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dsk_iid</inverse_name><rdb:join>MEASUREMENT_.DSK_IID IN (?)</rdb:join></relation_attribute><rdb:aid>10</rdb:aid><rdb:table>DISKS_</rdb:table></application_element><application_element><name>fremdmotor</name><basetype>AoUnitUnderTest</basetype><application_attribute><name>fremdmotor_iid</name><base_attribute>id</base_attribute><rdb:column>FREMDMOTOR__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>manuf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MANUF_</rdb:column></application_attribute><application_attribute><name>verbr_typ</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERBR_TYP_</rdb:column></application_attribute><application_attribute><name>zylzahl</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYLZAHL_</rdb:column></application_attribute><application_attribute><name>bauform</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BAUFORM_</rdb:column></application_attribute><application_attribute><name>besond</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BESOND_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>FREMDMOTOR_.FREMDMOTOR__IID IN (?) AND FREMDMOTOR_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>fremdmotor.exhaustsystem_iid</name><ref_to>fremdmotor.exhaustsystem</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:join>FREMDMOTOR_EXHAUSTSYSTEM.FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor.motoraufbau_iid</name><ref_to>fremdmotor.motoraufbau</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:join>FREMDMOTOR_MOTORAUFBAU.FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor.additional_iid</name><ref_to>fremdmotor.additional</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:join>FREMDMOTOR_ADDITIONAL.FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>DATA-SET_.DTS_IID=.DTS_IID AND .FREMDMOTOR__IID IN (?)</rdb:join></relation_attribute><rdb:aid>11</rdb:aid><rdb:table>FREMDMOTOR_</rdb:table></application_element><application_element><name>fremdmotor.exhaustsystem</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>fremdmotor.exhaustsystem_iid</name><base_attribute>id</base_attribute><rdb:column>FREMDMOTOR_EXHAUSTSYSTEM_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>elbow</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ELBOW_</rdb:column></application_attribute><application_attribute><name>inlet</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>INLET_</rdb:column></application_attribute><application_attribute><name>mixing</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MIXING_</rdb:column></application_attribute><application_attribute><name>cat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CAT_</rdb:column></application_attribute><application_attribute><name>dpf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>DPF_</rdb:column></application_attribute><application_attribute><name>fr_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>FR_MF_</rdb:column></application_attribute><application_attribute><name>tube2_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE2__</rdb:column></application_attribute><application_attribute><name>in_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IN_MF_</rdb:column></application_attribute><application_attribute><name>tube3_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE3__</rdb:column></application_attribute><application_attribute><name>rr_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>RR_MF_</rdb:column></application_attribute><application_attribute><name>outlet</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>OUTLET_</rdb:column></application_attribute><application_attribute><name>mount</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOUNT_</rdb:column></application_attribute><application_attribute><name>absorb</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABSORB_</rdb:column></application_attribute><application_attribute><name>suspen</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SUSPEN_</rdb:column></application_attribute><application_attribute><name>weitere</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>WEITERE_</rdb:column></application_attribute><application_attribute><name>tube1_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE1__</rdb:column></application_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>fremdmotor.exhaustsystem_iid</inverse_name><rdb:column>FREMDMOTOR__IID</rdb:column><rdb:join>FREMDMOTOR_EXHAUSTSYSTEM.FREMDMOTOR_EXHAUSTSYSTEM_IID IN (?) AND FREMDMOTOR_EXHAUSTSYSTEM.FREMDMOTOR__IID=FREMDMOTOR_.FREMDMOTOR__IID</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>fremdmotor.exhaustsystem_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .FREMDMOTOR_EXHAUSTSYSTEM_IID IN (?)</rdb:join></relation_attribute><rdb:aid>12</rdb:aid><rdb:table>FREMDMOTOR_EXHAUSTSYSTEM</rdb:table></application_element><application_element><name>motor</name><basetype>AoUnitUnderTest</basetype><application_attribute><name>motor_iid</name><base_attribute>id</base_attribute><rdb:column>MOTOR__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>motbegkart</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>MOTBEGKART_</rdb:column><rdb:sql>SELECT BLOB FROM MOTOR_ WHERE MOTOR__IID=?</rdb:sql></application_attribute><application_attribute><name>mtyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MTYP_</rdb:column></application_attribute><application_attribute><name>mtnr</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MTNR_</rdb:column></application_attribute><application_attribute><name>bauz</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BAUZ_</rdb:column></application_attribute><application_attribute><name>verbrenn</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERBRENN_</rdb:column></application_attribute><application_attribute><name>bauart</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BAUART_</rdb:column></application_attribute><application_attribute><name>fahrzeug</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>FAHRZEUG_</rdb:column></application_attribute><application_attribute><name>kennz</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KENNZ_</rdb:column></application_attribute><application_attribute><name>gtyp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GTYP_</rdb:column></application_attribute><application_attribute><name>gnr</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GNR_</rdb:column></application_attribute><application_attribute><name>riementr</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>RIEMENTR_</rdb:column></application_attribute><application_attribute><name>generator</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GENERATOR_</rdb:column></application_attribute><application_attribute><name>kmv</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KMV_</rdb:column></application_attribute><application_attribute><name>lhp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LHP_</rdb:column></application_attribute><application_attribute><name>abcpumpe</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABCPUMPE_</rdb:column></application_attribute><application_attribute><name>aggrsonst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>AGGRSONST_</rdb:column></application_attribute><application_attribute><name>besond</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>BESOND_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>motor_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>MOTOR_.MOTOR__IID IN (?) AND MOTOR_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>motor.exhaustsystem_iid</name><ref_to>motor.exhaustsystem</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:join>MOTOR_EXHAUSTSYSTEM.MOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor.motoraufbau_iid</name><ref_to>motor.motoraufbau</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:join>MOTOR_MOTORAUFBAU.MOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor.additional_iid</name><ref_to>motor.additional</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:join>MOTOR_ADDITIONAL.MOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .MOTOR__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>DATA-SET_.DTS_IID=.DTS_IID AND .MOTOR__IID IN (?)</rdb:join></relation_attribute><rdb:aid>13</rdb:aid><rdb:table>MOTOR_</rdb:table></application_element><application_element><name>motor.exhaustsystem</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>motor.exhaustsystem_iid</name><base_attribute>id</base_attribute><rdb:column>MOTOR_EXHAUSTSYSTEM_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>elbow</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ELBOW_</rdb:column></application_attribute><application_attribute><name>suspen</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SUSPEN_</rdb:column></application_attribute><application_attribute><name>inlet</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>INLET_</rdb:column></application_attribute><application_attribute><name>mixing</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MIXING_</rdb:column></application_attribute><application_attribute><name>cat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CAT_</rdb:column></application_attribute><application_attribute><name>dpf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>DPF_</rdb:column></application_attribute><application_attribute><name>tube1_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE1__</rdb:column></application_attribute><application_attribute><name>fr_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>FR_MF_</rdb:column></application_attribute><application_attribute><name>tube2_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE2__</rdb:column></application_attribute><application_attribute><name>in_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>IN_MF_</rdb:column></application_attribute><application_attribute><name>tube3_</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TUBE3__</rdb:column></application_attribute><application_attribute><name>rr_mf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>RR_MF_</rdb:column></application_attribute><application_attribute><name>outlet</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>OUTLET_</rdb:column></application_attribute><application_attribute><name>mount</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOUNT_</rdb:column></application_attribute><application_attribute><name>absorb</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABSORB_</rdb:column></application_attribute><application_attribute><name>weitere</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>WEITERE_</rdb:column></application_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>motor.exhaustsystem_iid</inverse_name><rdb:column>MOTOR__IID</rdb:column><rdb:join>MOTOR_EXHAUSTSYSTEM.MOTOR_EXHAUSTSYSTEM_IID IN (?) AND MOTOR_EXHAUSTSYSTEM.MOTOR__IID=MOTOR_.MOTOR__IID</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>motor.exhaustsystem_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .MOTOR_EXHAUSTSYSTEM_IID IN (?)</rdb:join></relation_attribute><rdb:aid>14</rdb:aid><rdb:table>MOTOR_EXHAUSTSYSTEM</rdb:table></application_element><application_element><name>messtext</name><basetype>AoTestSequence</basetype><application_attribute><name>messtext_iid</name><base_attribute>id</base_attribute><rdb:column>MESSTEXT__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>aufnehmer</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>AUFNEHMER_</rdb:column><rdb:sql>SELECT BLOB FROM MESSTEXT_ WHERE MESSTEXT__IID=?</rdb:sql></application_attribute><application_attribute><name>t</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>T_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><base_relation>environment</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>messtext_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>MESSTEXT_.MESSTEXT__IID IN (?) AND MESSTEXT_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>messtext_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .MESSTEXT__IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>messtext_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>DATA-SET_.DTS_IID=.DTS_IID AND .MESSTEXT__IID IN (?)</rdb:join></relation_attribute><rdb:aid>15</rdb:aid><rdb:table>MESSTEXT_</rdb:table></application_element><application_element><name>randbedingung</name><basetype>AoAny</basetype><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>randbedingung_iid</name><base_attribute>id</base_attribute><rdb:column>RANDBEDINGUNG__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>ftemp</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>FTEMP_</rdb:column></application_attribute><application_attribute><name>wgeschw</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>WGESCHW_</rdb:column></application_attribute><application_attribute><name>wricht</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>WRICHT_</rdb:column></application_attribute><application_attribute><name>mdat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MDAT_</rdb:column></application_attribute><application_attribute><name>mtim</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MTIM_</rdb:column></application_attribute><application_attribute><name>mort</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MORT_</rdb:column></application_attribute><application_attribute><name>prbes</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>PRBES_</rdb:column></application_attribute><application_attribute><name>ltemp</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>LTEMP_</rdb:column></application_attribute><application_attribute><name>ldruck</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>LDRUCK_</rdb:column></application_attribute><application_attribute><name>feuchte</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>FEUCHTE_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>randbedingung_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>RANDBEDINGUNG_.RANDBEDINGUNG__IID IN (?) AND RANDBEDINGUNG_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>mea-for-AnyId</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>AnyId</inverse_name><rdb:reftable/><rdb:refname>AnyId</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .RANDBEDINGUNG__IID IN (?)</rdb:join></relation_attribute><rdb:aid>16</rdb:aid><rdb:table>RANDBEDINGUNG_</rdb:table></application_element><application_element><name>messzyklus</name><basetype>AoAny</basetype><application_attribute><name>messzyklus_iid</name><base_attribute>id</base_attribute><rdb:column>MESSZYKLUS__IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>zyklus</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYKLUS_</rdb:column></application_attribute><application_attribute><name>zykl_ber</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYKL_BER_</rdb:column></application_attribute><application_attribute><name>lastzust</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LASTZUST_</rdb:column></application_attribute><application_attribute><name>kueb</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KUEB_</rdb:column></application_attribute><application_attribute><name>aufnehmer</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>AUFNEHMER_</rdb:column><rdb:sql>SELECT BLOB FROM MESSZYKLUS_ WHERE MESSZYKLUS__IID=?</rdb:sql></application_attribute><application_attribute><name>gang</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GANG_</rdb:column></application_attribute><application_attribute><name>kmvkomm</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KMVKOMM_</rdb:column></application_attribute><application_attribute><name>lhp</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LHP_</rdb:column></application_attribute><application_attribute><name>lhpkomm</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LHPKOMM_</rdb:column></application_attribute><application_attribute><name>lima</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LIMA_</rdb:column></application_attribute><application_attribute><name>limakomm</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LIMAKOMM_</rdb:column></application_attribute><application_attribute><name>measc</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MEASC_</rdb:column></application_attribute><application_attribute><name>kmv</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KMV_</rdb:column></application_attribute><relation_attribute><name>env_iid</name><ref_to>env</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>messzyklus_iid</inverse_name><rdb:column>ENV_IID</rdb:column><rdb:join>MESSZYKLUS_.MESSZYKLUS__IID IN (?) AND MESSZYKLUS_.ENV_IID=ENVIRONMENT_.ENV_IID</rdb:join></relation_attribute><relation_attribute><name>mea-for-CycId</name><ref_to>mea</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>CycId</inverse_name><rdb:reftable/><rdb:refname>CycId</rdb:refname><rdb:join>MEASUREMENT_.MEA_IID=.MEA_IID AND .MESSZYKLUS__IID IN (?)</rdb:join></relation_attribute><rdb:aid>17</rdb:aid><rdb:table>MESSZYKLUS_</rdb:table></application_element><application_element><name>mea</name><basetype>AoSubTest</basetype><application_attribute><name>postproc</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>POSTPROC</rdb:column></application_attribute><application_attribute><name>touchdate</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>TOUCHDATE</rdb:column></application_attribute><application_attribute><name>cirevision</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>CIREVISION</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>mea_iid</name><base_attribute>id</base_attribute><rdb:column>MEA_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><application_attribute><name>meastime</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MEASTIME</rdb:column></application_attribute><application_attribute><name>size</name><datatype>DT_LONG</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>SPACE</rdb:column></application_attribute><application_attribute><name>swrevision</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SWREVISION</rdb:column></application_attribute><application_attribute><name>prj</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>PRJ</rdb:column></application_attribute><application_attribute><name>tstser</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TSTSER</rdb:column></application_attribute><application_attribute><name>seq</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SEQ</rdb:column></application_attribute><application_attribute><name>ins</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>INS</rdb:column></application_attribute><application_attribute><name>uut</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>UUT</rdb:column></application_attribute><application_attribute><name>label</name><base_attribute>version</base_attribute><rdb:column>DSK_IID</rdb:column></application_attribute><application_attribute><name>measdate</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>MEASDATE</rdb:column></application_attribute><application_attribute><name>swstate</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SWSTATE</rdb:column></application_attribute><application_attribute><name>state</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>STATE</rdb:column></application_attribute><application_attribute><name>cidate</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CIDATE</rdb:column></application_attribute><application_attribute><name>discpath</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>DISCPATH</rdb:column></application_attribute><application_attribute><name>typ</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TYP</rdb:column></application_attribute><application_attribute><name>tstsubject</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TSTSUBJECT_</rdb:column></application_attribute><application_attribute><name>tststand</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TSTSTAND_</rdb:column></application_attribute><application_attribute><name>expert</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>EXPERT_</rdb:column></application_attribute><application_attribute><name>expertdiv</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>EXPERTDIV_</rdb:column></application_attribute><application_attribute><name>orderer</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ORDERER_</rdb:column></application_attribute><application_attribute><name>ordererdiv</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ORDERERDIV_</rdb:column></application_attribute><application_attribute><name>auftrdat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>AUFTRDAT_</rdb:column></application_attribute><application_attribute><name>ausfuehrdat</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>AUSFUEHRDAT_</rdb:column></application_attribute><application_attribute><name>kontierung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KONTIERUNG_</rdb:column></application_attribute><application_attribute><name>auftrnr</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>AUFTRNR_</rdb:column></application_attribute><relation_attribute><name>tstser_iid</name><ref_to>tstser</ref_to><base_relation>parent_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>mea_iid</inverse_name><rdb:column>TSTSER_IID</rdb:column><rdb:join>MEASUREMENT_.MEA_IID IN (?) AND MEASUREMENT_.TSTSER_IID=TEST_.TSTSER_IID</rdb:join></relation_attribute><relation_attribute><name>ins_iid</name><ref_to>ins</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>mea_iid</inverse_name><rdb:column>INS_IID</rdb:column><rdb:join>MEASUREMENT_.MEA_IID IN (?) AND MEASUREMENT_.INS_IID=SETUP_.INS_IID</rdb:join></relation_attribute><relation_attribute><name>dsk_iid</name><ref_to>dsk</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>mea_iid</inverse_name><rdb:column>DSK_IID</rdb:column><rdb:join>MEASUREMENT_.MEA_IID IN (?) AND MEASUREMENT_.DSK_IID=DISKS_.DSK_IID</rdb:join></relation_attribute><application_attribute><name>attachments</name><datatype>DS_EXTERNALREFERENCE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>ATTACHMENTS</rdb:column><rdb:value>'NULL'</rdb:value><rdb:sql>N/A</rdb:sql></application_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:join>DATA-SET_.MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>FREMDMOTOR_.FREMDMOTOR__IID=.FREMDMOTOR__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor.exhaustsystem_iid</name><ref_to>fremdmotor.exhaustsystem</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>FREMDMOTOR_EXHAUSTSYSTEM.FREMDMOTOR_EXHAUSTSYSTEM_IID=.FREMDMOTOR_EXHAUSTSYSTEM_IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MOTOR_.MOTOR__IID=.MOTOR__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor.exhaustsystem_iid</name><ref_to>motor.exhaustsystem</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MOTOR_EXHAUSTSYSTEM.MOTOR_EXHAUSTSYSTEM_IID=.MOTOR_EXHAUSTSYSTEM_IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>messtext_iid</name><ref_to>messtext</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MESSTEXT_.MESSTEXT__IID=.MESSTEXT__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>AnyId</name><ref_to>randbedingung</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea-for-AnyId</inverse_name><rdb:reftable/><rdb:refname>AnyId</rdb:refname><rdb:join>RANDBEDINGUNG_.RANDBEDINGUNG__IID=.RANDBEDINGUNG__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>CycId</name><ref_to>messzyklus</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>mea-for-CycId</inverse_name><rdb:reftable/><rdb:refname>CycId</rdb:refname><rdb:join>MESSZYKLUS_.MESSZYKLUS__IID=.MESSZYKLUS__IID AND .MEA_IID IN (?)</rdb:join></relation_attribute><rdb:aid>18</rdb:aid><rdb:table>MEASUREMENT_</rdb:table></application_element><application_element><name>pas</name><basetype>AoParameterSet</basetype><application_attribute><name>pas_iid</name><base_attribute>id</base_attribute><rdb:column>PAS_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>description</name><base_attribute>description</base_attribute><length>4000</length><rdb:column>DESCR</rdb:column></application_attribute><relation_attribute><name>par_iid</name><ref_to>par</ref_to><base_relation>parameters</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>pas_iid</inverse_name><rdb:join>PARAMETER_.PAS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>pas_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MEASURING QUANTITY_.MEQ_IID=.MEQ_IID AND .PAS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>pas_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>DATA-SET_.DTS_IID=.DTS_IID AND .PAS_IID IN (?)</rdb:join></relation_attribute><rdb:aid>19</rdb:aid><rdb:table>PARAMETER-SET_</rdb:table></application_element><application_element><name>dts</name><basetype>AoMeasurement</basetype><application_attribute><name>dts_iid</name><base_attribute>id</base_attribute><rdb:column>DTS_IID</rdb:column></application_attribute><application_attribute><name>measbegin</name><base_attribute>measurement_begin</base_attribute><rdb:column>MEASBEGIN</rdb:column></application_attribute><application_attribute><name>measend</name><base_attribute>measurement_end</base_attribute><rdb:column>MEASEND</rdb:column></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><relation_attribute><name>mea_iid</name><ref_to>mea</ref_to><base_relation>test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>dts_iid</inverse_name><rdb:column>MEA_IID</rdb:column><rdb:join>DATA-SET_.DTS_IID IN (?) AND DATA-SET_.MEA_IID=MEASUREMENT_.MEA_IID</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><base_relation>measurement_quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:join>MEASURING QUANTITY_.DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>pas_iid</name><ref_to>pas</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>PARAMETER-SET_.PAS_IID=.PAS_IID AND .DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>units_under_test</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>FREMDMOTOR_.FREMDMOTOR__IID=.FREMDMOTOR__IID AND .DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>units_under_test</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MOTOR_.MOTOR__IID=.MOTOR__IID AND .DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>messtext_iid</name><ref_to>messtext</ref_to><base_relation>sequences</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>MESSTEXT_.MESSTEXT__IID=.MESSTEXT__IID AND .DTS_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>sm_iid</name><ref_to>sm</ref_to><base_relation>submatrices</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>dts_iid</inverse_name></relation_attribute><relation_attribute><name>geometry</name><ref_to>geometry</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>measurements_on_geometry</inverse_name></relation_attribute><rdb:aid>20</rdb:aid><rdb:table>DATA-SET_</rdb:table></application_element><application_element><name>quantity</name><basetype>AoQuantity</basetype><application_attribute><name>quantity_iid</name><base_attribute>id</base_attribute><rdb:column>QUANTITY_IID</rdb:column></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><application_attribute><name>def_name</name><base_attribute>default_mq_name</base_attribute><length>4000</length><rdb:column>DEF_NAME</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>default_unit</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>quantity_iid</inverse_name><rdb:column>UNT_IID</rdb:column><rdb:join>QUANTITY_.QUANTITY_IID IN (?) AND QUANTITY_.UNT_IID=UNIT_.UNT_IID</rdb:join></relation_attribute><relation_attribute><name>pred_iid</name><ref_to>quantity</ref_to><base_relation>predecessor</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>suc_iid</inverse_name><rdb:column>PRED_IID</rdb:column><rdb:join>QUANTITY_.QUANTITY_IID=(SELECT QUANTITY_.PRED_IID FROM QUANTITY_ WHERE QUANTITY_.QUANTITY_IID IN (?))</rdb:join></relation_attribute><relation_attribute><name>suc_iid</name><ref_to>quantity</ref_to><base_relation>successors</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>pred_iid</inverse_name><rdb:join>QUANTITY_.PRED_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><base_relation>measurement_quantities</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>quantity_iid</inverse_name><rdb:join>MEASURING QUANTITY_.QUANTITY_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>grq_iid</name><ref_to>grq</ref_to><base_relation>groups</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>quantity_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>QUANTITYSYSTEM_.GRQ_IID=.GRQ_IID AND .QUANTITY_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq-for-non_reference_channel_quantity</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>non_reference_channel_quantity</inverse_name><rdb:reftable/><rdb:refname>non_reference_channel_quantity</rdb:refname><rdb:join>MEASURING QUANTITY_.MEQ_IID=.MEQ_IID AND .QUANTITY_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>meq-for-reference_channel_quantity</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>reference_channel_quantity</inverse_name><rdb:reftable/><rdb:refname>reference_channel_quantity</rdb:refname><rdb:join>MEASURING QUANTITY_.MEQ_IID=.MEQ_IID AND .QUANTITY_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>used_in_measurement_locations</name><ref_to>measurement_location</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>channel_quantity</inverse_name></relation_attribute><rdb:aid>21</rdb:aid><rdb:table>QUANTITY_</rdb:table></application_element><application_element><name>meq</name><basetype>AoMeasurementQuantity</basetype><application_attribute><name>meq_iid</name><base_attribute>id</base_attribute><rdb:column>MEQ_IID</rdb:column></application_attribute><application_attribute><name>aodt</name><base_attribute>datatype</base_attribute><rdb:column>AODT</rdb:column></application_attribute><application_attribute><name>non_reference_channel_name</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>NONREFCHN</rdb:column></application_attribute><application_attribute><name>reference_channel_name</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>REFCHN</rdb:column></application_attribute><application_attribute><name>min_val</name><base_attribute>minimum</base_attribute><rdb:column>MIN_VAL</rdb:column></application_attribute><application_attribute><name>max_val</name><base_attribute>maximum</base_attribute><rdb:column>MAX_VAL</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>description</name><base_attribute>description</base_attribute><length>4000</length><rdb:column>DESCR</rdb:column></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute><length>4000</length><rdb:column>MIME_TYPE</rdb:column></application_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>meq_iid</inverse_name><rdb:column>DTS_IID</rdb:column><rdb:join>MEASURING QUANTITY_.MEQ_IID IN (?) AND MEASURING QUANTITY_.DTS_IID=DATA-SET_.DTS_IID</rdb:join></relation_attribute><relation_attribute><name>quantity_iid</name><ref_to>quantity</ref_to><base_relation>quantity</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>meq_iid</inverse_name><rdb:column>QUANTITY_IID</rdb:column><rdb:join>MEASURING QUANTITY_.MEQ_IID IN (?) AND MEASURING QUANTITY_.QUANTITY_IID=QUANTITY_.QUANTITY_IID</rdb:join></relation_attribute><relation_attribute><name>sen_iid</name><ref_to>devchain</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>meq_iid</inverse_name><rdb:column>SEN_IID</rdb:column><rdb:join>MEASURING QUANTITY_.MEQ_IID IN (?) AND MEASURING QUANTITY_.SEN_IID=MEAS.DEV.CHAIN_.DEVCHAIN_IID</rdb:join></relation_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>unit</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>meq_iid</inverse_name><rdb:column>UNT_IID</rdb:column><rdb:join>MEASURING QUANTITY_.MEQ_IID IN (?) AND MEASURING QUANTITY_.UNT_IID=UNIT_.UNT_IID</rdb:join></relation_attribute><relation_attribute><name>non_reference_channel_quantity</name><ref_to>quantity</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>meq-for-non_reference_channel_quantity</inverse_name><rdb:reftable/><rdb:refname>non_reference_channel_quantity</rdb:refname><rdb:join>QUANTITY_.QUANTITY_IID=.QUANTITY_IID AND .MEQ_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>reference_channel_quantity</name><ref_to>quantity</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>meq-for-reference_channel_quantity</inverse_name><rdb:reftable/><rdb:refname>reference_channel_quantity</rdb:refname><rdb:join>QUANTITY_.QUANTITY_IID=.QUANTITY_IID AND .MEQ_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>pas_iid</name><ref_to>pas</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>meq_iid</inverse_name><rdb:reftable/><rdb:refname>base</rdb:refname><rdb:join>PARAMETER-SET_.PAS_IID=.PAS_IID AND .MEQ_IID IN (?)</rdb:join></relation_attribute><relation_attribute><name>lc_iid</name><ref_to>lc</ref_to><base_relation>local_columns</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>meq_iid</inverse_name></relation_attribute><relation_attribute><name>non_reference_locations</name><ref_to>measurement_location</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>in_reference_location</inverse_name></relation_attribute><relation_attribute><name>reference_locations</name><ref_to>measurement_location</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>in_non_reference_location</inverse_name></relation_attribute><rdb:aid>22</rdb:aid><rdb:table>MEASURING QUANTITY_</rdb:table></application_element><application_element><name>par</name><basetype>AoParameter</basetype><application_attribute><name>par_iid</name><base_attribute>id</base_attribute><rdb:column>PAR_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>pval</name><base_attribute>pvalue</base_attribute><length>4000</length><rdb:column>PVAL</rdb:column></application_attribute><application_attribute><name>ptyp</name><base_attribute>parameter_datatype</base_attribute><rdb:column>PTYP</rdb:column></application_attribute><relation_attribute><name>unt_iid</name><ref_to>unt</ref_to><base_relation>unit</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>par_iid</inverse_name><rdb:column>UNT_IID</rdb:column><rdb:join>PARAMETER_.PAR_IID IN (?) AND PARAMETER_.UNT_IID=UNIT_.UNT_IID</rdb:join></relation_attribute><relation_attribute><name>pas_iid</name><ref_to>pas</ref_to><base_relation>parameter_set</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>par_iid</inverse_name><rdb:column>PAS_IID</rdb:column><rdb:join>PARAMETER_.PAR_IID IN (?) AND PARAMETER_.PAS_IID=PARAMETER-SET_.PAS_IID</rdb:join></relation_attribute><rdb:aid>26</rdb:aid><rdb:table>PARAMETER_</rdb:table></application_element><application_element><name>fremdmotor.motoraufbau</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>fremdmotor.motoraufbau_iid</name><base_attribute>id</base_attribute><rdb:column>FREMDMOTOR_MOTORAUFBAU_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>kurbelwelle</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KURBELWELLE_</rdb:column></application_attribute><application_attribute><name>kolben</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KOLBEN_</rdb:column></application_attribute><application_attribute><name>ladeluftkueh</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LADELUFTKUEH_</rdb:column></application_attribute><application_attribute><name>ladeluftleit</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LADELUFTLEIT_</rdb:column></application_attribute><application_attribute><name>absblenden</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABSBLENDEN_</rdb:column></application_attribute><application_attribute><name>kraftstoff</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KRAFTSTOFF_</rdb:column></application_attribute><application_attribute><name>klopfregelung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KLOPFREGELUNG_</rdb:column></application_attribute><application_attribute><name>gelenkwelle</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GELENKWELLE_</rdb:column></application_attribute><application_attribute><name>sonst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SONST_</rdb:column></application_attribute><application_attribute><name>swstand</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SWSTAND_</rdb:column></application_attribute><application_attribute><name>abdeckung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABDECKUNG_</rdb:column></application_attribute><application_attribute><name>rohlufthutzen</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ROHLUFTHUTZEN_</rdb:column></application_attribute><application_attribute><name>luftfilter</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LUFTFILTER_</rdb:column></application_attribute><application_attribute><name>oelwanne</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>OELWANNE_</rdb:column></application_attribute><application_attribute><name>motorlager</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOTORLAGER_</rdb:column></application_attribute><application_attribute><name>motortraeger</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOTORTRAEGER_</rdb:column></application_attribute><application_attribute><name>getriebelager</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GETRIEBELAGER_</rdb:column></application_attribute><application_attribute><name>torsdaempf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TORSDAEMPF_</rdb:column></application_attribute><application_attribute><name>kurbelgeh</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KURBELGEH_</rdb:column></application_attribute><application_attribute><name>zylkopf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYLKOPF_</rdb:column></application_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>fremdmotor.motoraufbau_iid</inverse_name><rdb:column>FREMDMOTOR__IID</rdb:column><rdb:join>FREMDMOTOR_MOTORAUFBAU.FREMDMOTOR_MOTORAUFBAU_IID IN (?) AND FREMDMOTOR_MOTORAUFBAU.FREMDMOTOR__IID=FREMDMOTOR_.FREMDMOTOR__IID</rdb:join></relation_attribute><rdb:aid>27</rdb:aid><rdb:table>FREMDMOTOR_MOTORAUFBAU</rdb:table></application_element><application_element><name>fremdmotor.additional</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>fremdmotor.additional_iid</name><base_attribute>id</base_attribute><rdb:column>FREMDMOTOR_ADDITIONAL_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>add</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ADD_</rdb:column></application_attribute><application_attribute><name>auftrag</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>AUFTRAG_</rdb:column><rdb:sql>SELECT BLOB FROM FREMDMOTOR_ADDITIONAL WHERE FREMDMOTOR_ADDITIONAL_IID=?</rdb:sql></application_attribute><relation_attribute><name>fremdmotor_iid</name><ref_to>fremdmotor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>fremdmotor.additional_iid</inverse_name><rdb:column>FREMDMOTOR__IID</rdb:column><rdb:join>FREMDMOTOR_ADDITIONAL.FREMDMOTOR_ADDITIONAL_IID IN (?) AND FREMDMOTOR_ADDITIONAL.FREMDMOTOR__IID=FREMDMOTOR_.FREMDMOTOR__IID</rdb:join></relation_attribute><rdb:aid>28</rdb:aid><rdb:table>FREMDMOTOR_ADDITIONAL</rdb:table></application_element><application_element><name>motor.motoraufbau</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>motor.motoraufbau_iid</name><base_attribute>id</base_attribute><rdb:column>MOTOR_MOTORAUFBAU_IID</rdb:column></application_attribute><application_attribute><name>swstand</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SWSTAND_</rdb:column></application_attribute><application_attribute><name>abdeckung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABDECKUNG_</rdb:column></application_attribute><application_attribute><name>rohlufthutzen</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ROHLUFTHUTZEN_</rdb:column></application_attribute><application_attribute><name>luftfilter</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LUFTFILTER_</rdb:column></application_attribute><application_attribute><name>oelwanne</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>OELWANNE_</rdb:column></application_attribute><application_attribute><name>motorlager</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOTORLAGER_</rdb:column></application_attribute><application_attribute><name>motortraeger</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>MOTORTRAEGER_</rdb:column></application_attribute><application_attribute><name>getriebelager</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GETRIEBELAGER_</rdb:column></application_attribute><application_attribute><name>torsdaempf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>TORSDAEMPF_</rdb:column></application_attribute><application_attribute><name>zylkopf</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ZYLKOPF_</rdb:column></application_attribute><application_attribute><name>kurbelgeh</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KURBELGEH_</rdb:column></application_attribute><application_attribute><name>kurbelwelle</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KURBELWELLE_</rdb:column></application_attribute><application_attribute><name>kolben</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KOLBEN_</rdb:column></application_attribute><application_attribute><name>ladeluftkueh</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LADELUFTKUEH_</rdb:column></application_attribute><application_attribute><name>ladeluftleit</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>LADELUFTLEIT_</rdb:column></application_attribute><application_attribute><name>absblenden</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ABSBLENDEN_</rdb:column></application_attribute><application_attribute><name>kraftstoff</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KRAFTSTOFF_</rdb:column></application_attribute><application_attribute><name>klopfregelung</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>KLOPFREGELUNG_</rdb:column></application_attribute><application_attribute><name>sonst</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>SONST_</rdb:column></application_attribute><application_attribute><name>gelenkwelle</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>GELENKWELLE_</rdb:column></application_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>motor.motoraufbau_iid</inverse_name><rdb:column>MOTOR__IID</rdb:column><rdb:join>MOTOR_MOTORAUFBAU.MOTOR_MOTORAUFBAU_IID IN (?) AND MOTOR_MOTORAUFBAU.MOTOR__IID=MOTOR_.MOTOR__IID</rdb:join></relation_attribute><rdb:aid>29</rdb:aid><rdb:table>MOTOR_MOTORAUFBAU</rdb:table></application_element><application_element><name>motor.additional</name><basetype>AoUnitUnderTestPart</basetype><application_attribute><name>motor.additional_iid</name><base_attribute>id</base_attribute><rdb:column>MOTOR_ADDITIONAL_IID</rdb:column></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute><length>4000</length><rdb:column>INAME</rdb:column></application_attribute><application_attribute><name>changed</name><datatype>DT_DATE</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>CHANGED</rdb:column></application_attribute><application_attribute><name>version</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>VERS</rdb:column></application_attribute><application_attribute><name>add</name><datatype>DT_STRING</datatype><obligatory>false</obligatory><unique>false</unique><length>4000</length><rdb:column>ADD_</rdb:column></application_attribute><application_attribute><name>auftrag</name><datatype>DT_BLOB</datatype><obligatory>false</obligatory><unique>false</unique><rdb:column>AUFTRAG_</rdb:column><rdb:sql>SELECT BLOB FROM MOTOR_ADDITIONAL WHERE MOTOR_ADDITIONAL_IID=?</rdb:sql></application_attribute><relation_attribute><name>motor_iid</name><ref_to>motor</ref_to><base_relation>parent_unit_under_test</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>motor.additional_iid</inverse_name><rdb:column>MOTOR__IID</rdb:column><rdb:join>MOTOR_ADDITIONAL.MOTOR_ADDITIONAL_IID IN (?) AND MOTOR_ADDITIONAL.MOTOR__IID=MOTOR_.MOTOR__IID</rdb:join></relation_attribute><rdb:aid>30</rdb:aid><rdb:table>MOTOR_ADDITIONAL</rdb:table></application_element><application_element><name>sm</name><basetype>AoSubmatrix</basetype><application_attribute><name>sm_iid</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>mime_type</name><base_attribute>mime_type</base_attribute></application_attribute><application_attribute><name>number_of_rows</name><base_attribute>number_of_rows</base_attribute></application_attribute><relation_attribute><name>lc_iid</name><ref_to>lc</ref_to><base_relation>local_columns</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>sm_iid</inverse_name></relation_attribute><relation_attribute><name>dts_iid</name><ref_to>dts</ref_to><base_relation>measurement</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>sm_iid</inverse_name></relation_attribute><relation_attribute><name>x-axis-for-y-axis</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>y-axis-for-x-axis</inverse_name></relation_attribute><relation_attribute><name>z-axis-for-y-axis</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>y-axis-for-z-axis</inverse_name></relation_attribute><relation_attribute><name>y-axis-for-x-axis</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>x-axis-for-y-axis</inverse_name></relation_attribute><relation_attribute><name>y-axis-for-z-axis</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>z-axis-for-y-axis</inverse_name></relation_attribute><relation_attribute><name>geometry</name><ref_to>geometry</ref_to><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>geometry_shapes</inverse_name></relation_attribute></application_element><application_element><name>lc</name><basetype>AoLocalColumn</basetype><application_attribute><name>lc_iid</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>independent</name><base_attribute>independent</base_attribute></application_attribute><application_attribute><name>values</name><base_attribute>values</base_attribute></application_attribute><application_attribute><name>sequence_representation</name><base_attribute>sequence_representation</base_attribute></application_attribute><application_attribute><name>generation_parameters</name><base_attribute>generation_parameters</base_attribute></application_attribute><application_attribute><name>raw_datatype</name><base_attribute>raw_datatype</base_attribute></application_attribute><application_attribute><name>axistype</name><datatype>DT_ENUM</datatype><enumeration_type>axistype</enumeration_type></application_attribute><application_attribute><name>global_flag</name><base_attribute>global_flag</base_attribute></application_attribute><application_attribute><name>flags</name><base_attribute>flags</base_attribute></application_attribute><relation_attribute><name>ec_iid</name><ref_to>ec</ref_to><base_relation>external_component</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>lc_iid</inverse_name></relation_attribute><relation_attribute><name>sm_iid</name><ref_to>sm</ref_to><base_relation>submatrix</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>lc_iid</inverse_name></relation_attribute><relation_attribute><name>meq_iid</name><ref_to>meq</ref_to><base_relation>measurement_quantity</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>lc_iid</inverse_name></relation_attribute></application_element><application_element><name>ec</name><basetype>AoExternalComponent</basetype><application_attribute><name>ec_iid</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>iname</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>description</name><base_attribute>description</base_attribute></application_attribute><application_attribute><name>ordinal_number</name><base_attribute>ordinal_number</base_attribute></application_attribute><application_attribute><name>component_length</name><base_attribute>component_length</base_attribute></application_attribute><application_attribute><name>filename_url</name><base_attribute>filename_url</base_attribute></application_attribute><application_attribute><name>value_type</name><base_attribute>value_type</base_attribute></application_attribute><application_attribute><name>start_offset</name><base_attribute>start_offset</base_attribute></application_attribute><application_attribute><name>block_size</name><base_attribute>block_size</base_attribute></application_attribute><application_attribute><name>valuesperblock</name><base_attribute>valuesperblock</base_attribute></application_attribute><application_attribute><name>value_offset</name><base_attribute>value_offset</base_attribute></application_attribute><relation_attribute><name>lc_iid</name><ref_to>lc</ref_to><base_relation>local_column</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>ec_iid</inverse_name></relation_attribute></application_element><application_element><name>coordinate_system</name><basetype>AoAny</basetype><application_attribute><name>id</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>name</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute></application_attribute><application_attribute><name>coordinate_system_type</name><datatype>DT_ENUM</datatype><enumeration_type>coordinate_system_types</enumeration_type></application_attribute><application_attribute><name>origin_1</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>origin_2</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>origin_3</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>x_axis_1</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>x_axis_2</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>x_axis_3</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>xz_plane_1</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>xz_plane_2</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><application_attribute><name>xz_plane_3</name><datatype>DT_DOUBLE</datatype><obligatory>false</obligatory></application_attribute><relation_attribute><name>reference_system</name><ref_to>coordinate_system</ref_to><base_relation>parent</base_relation><min_occurs>0</min_occurs><max_occurs>1</max_occurs><inverse_name>locale_systems</inverse_name></relation_attribute><relation_attribute><name>locale_systems</name><ref_to>coordinate_system</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>reference_system</inverse_name></relation_attribute><relation_attribute><name>used_in_geometries</name><ref_to>geometry</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>coordinate_system</inverse_name></relation_attribute><relation_attribute><name>used_in_measurement_locations</name><ref_to>measurement_location</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>coordinate_system</inverse_name></relation_attribute></application_element><application_element><name>geometry</name><basetype>AoAny</basetype><application_attribute><name>id</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>name</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>version</name><base_attribute>version</base_attribute></application_attribute><application_attribute><name>geometry_id</name><datatype>DT_LONG</datatype></application_attribute><relation_attribute><name>coordinate_system</name><ref_to>coordinate_system</ref_to><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>used_in_geometries</inverse_name></relation_attribute><relation_attribute><name>sub_geometries</name><ref_to>geometry</ref_to><base_relation>children</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>parent_geometry</inverse_name></relation_attribute><relation_attribute><name>parent_geometry</name><ref_to>geometry</ref_to><base_relation>parent</base_relation><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>sub_geometries</inverse_name></relation_attribute><relation_attribute><name>measurements_on_geometry</name><ref_to>dts</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>geometry</inverse_name></relation_attribute><relation_attribute><name>geometry_shapes</name><ref_to>sm</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>geometry</inverse_name></relation_attribute></application_element><application_element><name>measurement_location</name><basetype>AoTestEquipmentPart</basetype><application_attribute><name>id</name><base_attribute>id</base_attribute></application_attribute><application_attribute><name>name</name><base_attribute>name</base_attribute></application_attribute><application_attribute><name>location_shape_id</name><datatype>DT_LONG</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>location_geometry_id</name><datatype>DT_LONG</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>location_mode</name><datatype>DT_ENUM</datatype><enumeration_type>location_modes</enumeration_type><obligatory>true</obligatory></application_attribute><application_attribute><name>channel_name</name><datatype>DT_STRING</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>channel_description</name><datatype>DT_STRING</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>u</name><datatype>DT_FLOAT</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>v</name><datatype>DT_FLOAT</datatype><obligatory>true</obligatory></application_attribute><application_attribute><name>w</name><datatype>DT_FLOAT</datatype><obligatory>true</obligatory></application_attribute><relation_attribute><name>coordinate_system</name><ref_to>coordinate_system</ref_to><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>used_in_measurement_locations</inverse_name></relation_attribute><relation_attribute><name>channel_quantity</name><ref_to>quantity</ref_to><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>used_in_measurement_locations</inverse_name></relation_attribute><relation_attribute><name>in_reference_location</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>reference_locations</inverse_name></relation_attribute><relation_attribute><name>in_non_reference_location</name><ref_to>meq</ref_to><min_occurs>0</min_occurs><max_occurs>Many</max_occurs><inverse_name>non_reference_locations</inverse_name></relation_attribute><relation_attribute><name>devchain_iid</name><ref_to>devchain</ref_to><base_relation>parent_equipment_part</base_relation><min_occurs>1</min_occurs><max_occurs>1</max_occurs><inverse_name>measurement_locations</inverse_name></relation_attribute></application_element></application_model></atfx_file>
+
diff --git a/org.eclipse.mdm.filerelease/src/main/configuration/TSA/model.atfx.technical b/org.eclipse.mdm.filerelease/src/main/configuration/TSA/model.atfx.technical
new file mode 100644
index 0000000..5d5a5f4
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/configuration/TSA/model.atfx.technical
Binary files differ
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/boundary/FileReleaseResource.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/boundary/FileReleaseResource.java
new file mode 100644
index 0000000..fdd7679
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/boundary/FileReleaseResource.java
@@ -0,0 +1,185 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.filerelease.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.POST;
+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.filerelease.control.FileReleaseManager;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.eclipse.mdm.filerelease.entity.FileReleaseResponse;
+import org.eclipse.mdm.filerelease.utils.FileReleaseUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link FileRelease} resource
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Path("/filereleases")
+public class FileReleaseResource {
+
+	private static final Logger LOG = LoggerFactory.getLogger(FileReleaseResource.class);
+
+	@EJB
+	private FileReleaseService fileReleaseService;
+
+	/**
+	 * 
+	 * delegates the request to the {@link FileReleaseService}
+	 * 
+	 * @param identifier
+	 *            The identifier of the {@link FileRelease}
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("/{IDENTIFIER}")
+	public Response getRelease(@PathParam("IDENTIFIER") String identifier) {
+		try {
+			FileRelease fileRelease = this.fileReleaseService.getRelease(identifier);
+			return FileReleaseUtils.toResponse(new FileReleaseResponse(fileRelease), 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 FileReleaseService}
+	 * 
+	 * @param state
+	 *            The state of the {@link FileRelease}s to return
+	 * @param direction
+	 *            The file release direction (incoming or outgoing)
+	 * @return @return the result of the delegated request as {@link Response}
+	 */
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	public Response getReleases(@QueryParam("direction") String direction, @QueryParam("state") String state) {
+		try {
+
+			List<FileRelease> list = null;
+
+			if ((direction != null)
+					&& direction.equalsIgnoreCase(FileReleaseManager.FILE_RELEASE_DIRECTION_INCOMMING)) {
+				list = this.fileReleaseService.getIncommingReleases(state);
+			} else if ((direction != null)
+					&& direction.equalsIgnoreCase(FileReleaseManager.FILE_RELEASE_DIRECTION_OUTGOING)) {
+				list = this.fileReleaseService.getOutgoingReleases(state);
+			} else {
+				list = this.fileReleaseService.getReleases(state);
+			}
+
+			return FileReleaseUtils.toResponse(new FileReleaseResponse(list), 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 FileReleaseService}
+	 * 
+	 * @param newFileRelease
+	 *            The {@link FileReleaseRequest} to create.
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@POST
+	@Consumes(MediaType.APPLICATION_JSON)
+	public Response create(FileRelease newFileRelease) {
+		try {
+			FileRelease fileRelease = this.fileReleaseService.create(newFileRelease);
+			return FileReleaseUtils.toResponse(new FileReleaseResponse(fileRelease), 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 FileReleaseService}
+	 * 
+	 * @param identifier
+	 *            The identifier of the {@link FileRelease} to update.
+	 * @param The
+	 *            {@link FileRelease} with updated state
+	 * @return the result of the delegated request as {@link Response}
+	 */
+	@POST
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{IDENTIFIER}")
+	public Response update(@PathParam("IDENTIFIER") String identifier, FileRelease updatedFileRelease) {
+		try {
+
+			if (!identifier.equals(updatedFileRelease.identifier)) {
+				throw new WebApplicationException("illegal update post request (identifier is not matching)",
+						Status.FORBIDDEN);
+			}
+
+			if (updatedFileRelease.state.equalsIgnoreCase(FileReleaseManager.FILE_RELEASE_STATE_APPROVED)) {
+				FileRelease fr = this.fileReleaseService.approve(updatedFileRelease);
+				return FileReleaseUtils.toResponse(new FileReleaseResponse(fr), Status.OK);
+			} else if (updatedFileRelease.state.equalsIgnoreCase(FileReleaseManager.FILE_RELEASE_STATE_REJECTED)) {
+				FileRelease fr = this.fileReleaseService.reject(updatedFileRelease);
+				return FileReleaseUtils.toResponse(new FileReleaseResponse(fr), Status.OK);
+			}
+			String errorMessage = "permission denied: only state updates are allowd "
+					+ "(expected stats: RELEASE_APPROVED or RELEASE_REJECTED";
+			throw new WebApplicationException(errorMessage, Status.FORBIDDEN);
+
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+	/**
+	 * delegates the request to the {@link FileReleaseService}
+	 * 
+	 * @param identifier
+	 *            The identifier of the {@link FileRelease} to delete.
+	 * @return the result of the delegated request as {@link Response} (only OK
+	 *         if {@link FileRelease} has been deleted)
+	 */
+	@DELETE
+	@Path("/{IDENTIFIER}")
+	public Response delete(@PathParam("IDENTIFIER") String identifier) {
+		try {
+			this.fileReleaseService.delete(identifier);
+			return Response.ok().build();
+		} catch (RuntimeException e) {
+			LOG.error(e.getMessage(), e);
+			throw new WebApplicationException(e.getMessage(), e, Status.INTERNAL_SERVER_ERROR);
+		}
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/boundary/FileReleaseService.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/boundary/FileReleaseService.java
new file mode 100644
index 0000000..e20527e
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/boundary/FileReleaseService.java
@@ -0,0 +1,257 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.boundary;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.model.User;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.eclipse.mdm.filerelease.control.FileConvertJobManager;
+import org.eclipse.mdm.filerelease.control.FileReleaseException;
+import org.eclipse.mdm.filerelease.control.FileReleaseManager;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.eclipse.mdm.filerelease.utils.FileReleasePermissionUtils;
+import org.eclipse.mdm.filerelease.utils.FileReleaseUtils;
+import org.eclipse.mdm.property.GlobalProperty;
+
+/**
+ * FileReleaseService Bean implementation with available {@link FileRelease}
+ * operations
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class FileReleaseService {
+
+	@Inject
+	private ConnectorService connectorService;
+	@EJB
+	private FileReleaseManager manager;
+	@EJB
+	private FileConvertJobManager converter;
+
+	@Inject
+	@GlobalProperty("filerelease.converter.target.root.directory")
+	private String targetDirectoryPath = "";
+
+	/**
+	 * Returns the the {@link FileRelease} with the given identifier
+	 * 
+	 * @param identifier
+	 *            The identifier of the {@link FileRelease}
+	 * @return The {@link FileRelease}
+	 */
+	public FileRelease getRelease(String identifier) {
+		User user = FileReleaseUtils.getLoggedOnUser(this.connectorService);
+		return this.manager.getRelease(user.getName(), identifier);
+	}
+
+	/**
+	 * Returns all {@link FileRelease}s with the given state.
+	 * 
+	 * @param state
+	 *            The state of the {@link FileRelease}s to return (null means
+	 *            all releases in every state will be returned)
+	 * @return The {@link FileRelease}s in given state or all
+	 *         {@link FileRelease}s
+	 */
+	public List<FileRelease> getReleases(String state) {
+		Set<FileRelease> fileReleases = new HashSet<>();
+		fileReleases.addAll(getIncommingReleases(state));
+		fileReleases.addAll(getOutgoingReleases(state));
+		return new ArrayList<>(fileReleases);
+	}
+
+	/**
+	 * Returns all incoming {@link FileRelease}s with the given state.
+	 * 
+	 * @param state
+	 *            The state of the incoming {@link FileRelease}s to return (null
+	 *            means all incoming releases in every state will be returned)
+	 * @return The incoming {@link FileRelease} in given state or all incoming
+	 *         {@link FileRelease}s
+	 */
+	public List<FileRelease> getIncommingReleases(String state) {
+
+		User user = FileReleaseUtils.getLoggedOnUser(this.connectorService);
+		if (state == null || state.trim().length() <= 0) {
+			List<FileRelease> list = this.manager.getReleases(user.getName(),
+					FileReleaseManager.FILE_RELEASE_DIRECTION_INCOMMING);
+			return FileReleaseUtils.filterByConnectedSources(list, this.connectorService);
+		}
+		List<FileRelease> list = this.manager.getReleases(user.getName(),
+				FileReleaseManager.FILE_RELEASE_DIRECTION_INCOMMING, state);
+		return FileReleaseUtils.filterByConnectedSources(list, this.connectorService);
+	}
+
+	/**
+	 * Returns all outgoing {@link FileRelease}s with the given state.
+	 * 
+	 * @param state
+	 *            The state of the outgoing {@link FileRelease}s to return (null
+	 *            means all outgoing releases in every state will be returned)
+	 * @return The outgoing {@link FileRelease}s in given state or all outgoing
+	 *         {@link FileRelease}s
+	 */
+	public List<FileRelease> getOutgoingReleases(String state) {
+
+		User user = FileReleaseUtils.getLoggedOnUser(this.connectorService);
+		if (state == null || state.trim().length() <= 0) {
+			List<FileRelease> list = this.manager.getReleases(user.getName(),
+					FileReleaseManager.FILE_RELEASE_DIRECTION_OUTGOING);
+			return FileReleaseUtils.filterByConnectedSources(list, this.connectorService);
+		}
+		List<FileRelease> list = this.manager.getReleases(user.getName(),
+				FileReleaseManager.FILE_RELEASE_DIRECTION_OUTGOING, state);
+		return FileReleaseUtils.filterByConnectedSources(list, this.connectorService);
+	}
+
+	/**
+	 * Creates a new {@link FileRelease}. Approves the {@link FileRelease}
+	 * directly if the sender is equals the receiver of the {@link FileRelease}.
+	 * 
+	 * @param newFileRelease
+	 *            The given {@link FileRelease} that holds the information to
+	 *            create a new {@link FileRelease}
+	 */
+	public FileRelease create(FileRelease newFileRelease) {
+		checkFileReleaseRequest(newFileRelease);
+		User user = FileReleaseUtils.getLoggedOnUser(this.connectorService);
+
+		List<FileRelease> list = this.manager.getReleases(user.getName(),
+				FileReleaseManager.FILE_RELEASE_DIRECTION_OUTGOING);
+		FileReleasePermissionUtils.canCreate(newFileRelease, user.getName(), list);
+
+		TestStep testStep = FileReleaseUtils.loadTestStep(connectorService, newFileRelease.sourceName,
+				newFileRelease.id);
+		User receiver = FileReleaseUtils.getResponsiblePerson(this.connectorService, testStep);
+
+		newFileRelease.identifier = UUID.randomUUID().toString();
+		newFileRelease.name = testStep.getName();
+		newFileRelease.state = FileReleaseManager.FILE_RELEASE_STATE_ORDERED;
+		newFileRelease.sender = user.getName();
+		newFileRelease.receiver = receiver.getName();
+
+		this.manager.addFileRelease(newFileRelease);
+
+		if (newFileRelease.sender.equalsIgnoreCase(newFileRelease.receiver)) {
+			FileRelease fileRelease2Approve = new FileRelease();
+			fileRelease2Approve.identifier = newFileRelease.identifier;
+			fileRelease2Approve.state = FileReleaseManager.FILE_RELEASE_STATE_APPROVED;
+			approve(fileRelease2Approve);
+		}
+
+		return newFileRelease;
+	}
+
+	/**
+	 * Deletes the {@link FileRelease} with the given identifier.
+	 * 
+	 * @param identifier
+	 *            The identifier of the {@link FileRelease} to delete.
+	 */
+	public void delete(String identifier) {
+
+		User user = FileReleaseUtils.getLoggedOnUser(this.connectorService);
+		FileRelease fileRelease = this.manager.getRelease(user.getName(), identifier);
+
+		FileReleasePermissionUtils.canDelete(fileRelease, user.getName());
+		File targetDirectory = FileReleaseUtils.locateTargetDirectory(this.targetDirectoryPath);
+		File targetFile = new File(targetDirectory, fileRelease.fileLink);
+		if (this.manager.canDeleteFileLink(fileRelease.identifier, fileRelease.fileLink)) {
+			FileReleaseUtils.deleteFileLink(targetFile);
+		}
+
+		this.manager.removeFileRelease(fileRelease);
+	}
+
+	/**
+	 * Approves and Releases the {@link FileRelease}
+	 * 
+	 * @param updatedFileRelease
+	 *            The {@link FileRelease} to approve and release
+	 */
+	public FileRelease approve(FileRelease updatedFileRelease) {
+
+		User user = FileReleaseUtils.getLoggedOnUser(this.connectorService);
+		FileRelease fileRelease = this.manager.getRelease(user.getName(), updatedFileRelease.identifier);
+
+		FileReleasePermissionUtils.canApprove(fileRelease, user.getName());
+		fileRelease.state = updatedFileRelease.state;
+
+		FileReleasePermissionUtils.canRelease(fileRelease, user.getName());
+		this.converter.release(fileRelease, FileReleaseUtils.locateTargetDirectory(this.targetDirectoryPath));
+		return fileRelease;
+	}
+
+	/**
+	 * Rejects the {@link FileRelease} with the given identifier. Sets the given
+	 * message as reject message.
+	 * 
+	 * @param updatedFileRelease
+	 *            {@link FileRelease} to reject
+	 */
+	public FileRelease reject(FileRelease updatedFileRelease) {
+
+		User user = FileReleaseUtils.getLoggedOnUser(this.connectorService);
+		FileRelease fileRelease = this.manager.getRelease(user.getName(), updatedFileRelease.identifier);
+
+		FileReleasePermissionUtils.canReject(fileRelease, user.getName());
+		fileRelease.state = updatedFileRelease.state;
+		fileRelease.rejectMessage = updatedFileRelease.rejectMessage;
+		return fileRelease;
+	}
+
+	private void checkFileReleaseRequest(FileRelease newFileRelease) {
+		if (newFileRelease.sourceName == null || newFileRelease.sourceName.trim().length() < 1) {
+			throw new FileReleaseException("source name for new FileRelease is missing!");
+		}
+
+		if (newFileRelease.typeName == null || newFileRelease.typeName.trim().length() < 1) {
+			throw new FileReleaseException("type name for new FileRelease is missing!");
+		}
+
+		if (newFileRelease.id == null || newFileRelease.id.isEmpty()) {
+			throw new FileReleaseException("is is not valid for new FileRelease");
+		}
+
+		if (newFileRelease.validity <= 0) {
+			throw new FileReleaseException("validity [days] is not set for new FileRelease");
+		}
+
+		if (newFileRelease.format == null || newFileRelease.format.trim().length() <= 0) {
+			throw new FileReleaseException("output format for new FileRelease is missing!");
+		}
+
+		if (!FileReleaseUtils.isFormatValid(newFileRelease.format)) {
+			throw new FileReleaseException(
+					"unsupported file output format '" + newFileRelease.format + "' was defined for new FileRelease");
+		}
+
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileConvertJob.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileConvertJob.java
new file mode 100644
index 0000000..29f13c2
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileConvertJob.java
@@ -0,0 +1,103 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control;
+
+import java.io.File;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.filerelease.control.converter.FileConverterException;
+import org.eclipse.mdm.filerelease.control.converter.IFileConverter;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Job for converting files.
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class FileConvertJob implements Runnable {
+
+	private static final Logger LOG = LoggerFactory.getLogger(FileConvertJob.class);
+
+	private final IFileConverter fileConverter;
+	private final FileRelease fileRelease;
+	private final TestStep testStep;
+	private final ApplicationContext context;
+	private final File targetDirectory;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param fileRelease
+	 *            The {@link FileRelease} that contains the link to the file to
+	 *            convert
+	 * @param fileConverter
+	 *            The {@link IFileConverter} to use for the conversion
+	 * @param testStep
+	 *            The {@link TestStep}
+	 * @param em
+	 *            The {@link EntityManager}
+	 * @param targetDirectory
+	 *            target output directory for the generated files
+	 */
+	public FileConvertJob(FileRelease fileRelease, IFileConverter fileConverter, TestStep testStep, ApplicationContext context,
+			File targetDirectory) {
+
+		this.fileRelease = fileRelease;
+		this.fileConverter = fileConverter;
+		this.testStep = testStep;
+		this.context = context;
+		this.targetDirectory = targetDirectory;
+	}
+
+	/**
+	 * Executes the file conversion.
+	 */
+	@Override
+	public void run() {
+		try {
+			this.fileRelease.state = FileReleaseManager.FILE_RELEASE_STATE_PROGRESSING;
+
+			this.fileConverter.execute(this.fileRelease, this.testStep, this.context, this.targetDirectory);
+
+			this.fileRelease.expire = calculateExpireDate(this.fileRelease.validity);
+			this.fileRelease.state = FileReleaseManager.FILE_RELEASE_STATE_RELEASED;
+
+		} catch (FileConverterException e) {
+			this.fileRelease.state = FileReleaseManager.FILE_RELEASE_STATE_PROGRESSING_ERROR;
+			this.fileRelease.errorMessage = e.getMessage();
+			LOG.error(e.getMessage(), e);
+		} catch (Exception e) {
+			this.fileRelease.state = FileReleaseManager.FILE_RELEASE_STATE_PROGRESSING_ERROR;
+			this.fileRelease.errorMessage = e.getMessage();
+			LOG.error(e.getMessage(), e);
+		}
+	}
+
+	private long calculateExpireDate(int validity) {
+		Calendar calendar = new GregorianCalendar();
+		calendar.setTime(new Date());
+		calendar.add(Calendar.DAY_OF_MONTH, validity);
+		return calendar.getTimeInMillis();
+	}
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileConvertJobManager.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileConvertJobManager.java
new file mode 100644
index 0000000..96fa7ac
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileConvertJobManager.java
@@ -0,0 +1,104 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control;
+
+import java.io.File;
+import java.util.concurrent.Executor;
+
+import javax.ejb.Stateless;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.eclipse.mdm.filerelease.control.converter.FileConverterPAK2ATFX;
+import org.eclipse.mdm.filerelease.control.converter.FileConverterPAK2RAW;
+import org.eclipse.mdm.filerelease.control.converter.IFileConverter;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * FileConvertJobManager Bean implementation.
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class FileConvertJobManager {
+
+	private static final Logger LOG = LoggerFactory.getLogger(FileConvertJobManager.class);
+
+	@Inject
+	Executor executor;
+
+	@Inject
+	private ConnectorService connectorService;
+
+	@Inject
+	private FileConverterPAK2RAW fileConverterPAK2RAW;
+	@Inject
+	private FileConverterPAK2ATFX fileConverterPAK2ATFX;
+
+	/**
+	 * releases the given {@link FileRelease} (generates the file in the
+	 * specified format)
+	 * 
+	 * @param fileRelease
+	 *            {@link FileRelease} to release
+	 * @param targetDirectory
+	 *            target output directory of the generated file
+	 */
+	public void release(FileRelease fileRelease, File targetDirectory) {
+
+		try {
+			ApplicationContext context = this.connectorService.getContextByName(fileRelease.sourceName);
+			EntityManager em = context
+					.getEntityManager()
+					.orElseThrow(() -> new FileReleaseException("Entity manager not present!"));
+			
+			TestStep testStep = em.load(TestStep.class, fileRelease.id);
+
+			IFileConverter converter = getFileConverterByFormat(fileRelease);
+			String identifier = fileRelease.identifier;
+
+			LOG.info("starting file release process for FileRelease with identifier '" + identifier + "' (with '"
+					+ converter.getConverterName() + "') ...");
+
+			Runnable runnable = new FileConvertJob(fileRelease, converter, testStep, context, targetDirectory);
+			this.executor.execute(runnable);
+		} catch (DataAccessException e) {
+			throw new FileReleaseException(e.getMessage(), e);
+		}
+
+	}
+
+	private IFileConverter getFileConverterByFormat(FileRelease fileRelease) {
+		if (fileRelease.format.equalsIgnoreCase(FileReleaseManager.CONVERTER_FORMAT_PAK2RAW)) {
+			return this.fileConverterPAK2RAW;
+
+		} else if (fileRelease.format.equalsIgnoreCase(FileReleaseManager.CONVERTER_FORMAT_PAK2ATFX)) {
+			return this.fileConverterPAK2ATFX;
+		}
+
+		throw new FileReleaseException("no FileConverter found for format '" + fileRelease.format
+				+ "' on executing FileRelease with identifier '" + fileRelease.identifier + "'!");
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleaseException.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleaseException.java
new file mode 100644
index 0000000..0a5b1db
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleaseException.java
@@ -0,0 +1,49 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control;
+
+/**
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class FileReleaseException extends RuntimeException {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param message
+	 *            The error message.
+	 */
+	public FileReleaseException(String message) {
+		super(message);
+	}
+
+	/**
+	 * Constructor
+	 * 
+	 * @param message
+	 *            The error message
+	 * @param t
+	 *            The {@link Throwable} that caused the exception
+	 */
+	public FileReleaseException(String message, Throwable t) {
+		super(message, t);
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleaseExecutor.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleaseExecutor.java
new file mode 100644
index 0000000..5fe656b
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleaseExecutor.java
@@ -0,0 +1,39 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control;
+
+import java.util.concurrent.Executor;
+
+import javax.ejb.Asynchronous;
+import javax.ejb.Stateless;
+
+/**
+ * 
+ * {@link Executor} implementation.
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class FileReleaseExecutor implements Executor {
+
+	@Override
+	@Asynchronous
+	public void execute(Runnable command) {
+		command.run();
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleaseManager.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleaseManager.java
new file mode 100644
index 0000000..48e6359
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleaseManager.java
@@ -0,0 +1,254 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.ejb.EJB;
+import javax.ejb.Singleton;
+import javax.ejb.Startup;
+
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+ * FileReleaseManager bean implementation.
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Startup
+@Singleton
+public class FileReleaseManager {
+
+	private static final Logger LOG = LoggerFactory.getLogger(FileReleaseManager.class);
+
+	public static final String FILE_RELEASE_STATE_ORDERED = "RELEASE_ORDERED";
+	public static final String FILE_RELEASE_STATE_REJECTED = "RELEASE_REJECTED";
+	public static final String FILE_RELEASE_STATE_APPROVED = "RELEASE_APPROVED";
+	public static final String FILE_RELEASE_STATE_PROGRESSING = "RELEASE_PROGRESSING";
+	public static final String FILE_RELEASE_STATE_PROGRESSING_ERROR = "RELEASE_PROGRESSING_ERROR";
+	public static final String FILE_RELEASE_STATE_RELEASED = "RELEASE_RELEASED";
+	public static final String FILE_RELEASE_STATE_EXPIRED = "RELEASE_EXPIRED";
+
+	public static final String FILE_RELEASE_DIRECTION_INCOMMING = "INCOMMING";
+	public static final String FILE_RELEASE_DIRECTION_OUTGOING = "OUTGOING";
+
+	public static final String CONVERTER_FORMAT_PAK2RAW = "PAK2RAW";
+	public static final String CONVERTER_FORMAT_PAK2ATFX = "PAK2ATFX";
+
+	private Map<String, FileRelease> releaseMap;
+
+	@EJB
+	private FileReleasePersistance releasePersistance;
+
+	/**
+	 * Returns the {@link FileRelease} for the given user with the given
+	 * identifier.
+	 * 
+	 * @param userName
+	 *            The name of the user to locate the {@link FileRelease}
+	 * @param identifier
+	 *            The identifier of the {@link FileRelease}
+	 * @return The found {@link FileRelease}
+	 */
+	public FileRelease getRelease(String userName, String identifier) {
+
+		if (!this.releaseMap.containsKey(identifier)) {
+			throw new FileReleaseException("unable to find FileRelease with identifier '" + identifier + "'!");
+		}
+
+		FileRelease fileRelease = this.releaseMap.get(identifier);
+
+		if (fileRelease.sender.equalsIgnoreCase(userName) || fileRelease.receiver.equalsIgnoreCase(userName)) {
+			updateExpired(fileRelease);
+			return fileRelease;
+		}
+
+		throw new FileReleaseException("unable to find FileRelease with identifier '" + identifier
+				+ "' for user with name '" + userName + "'!");
+
+	}
+
+	/**
+	 * 
+	 * Returns all {@link FileRelease}s for the given user in the given
+	 * direction ({@link FileReleaseManager#FILE_RELEASE_DIRECTION_INCOMMING} or
+	 * ({@link FileReleaseManager#FILE_RELEASE_DIRECTION_OUTGOING}).
+	 * 
+	 * @param userName
+	 *            The name of the user
+	 * @param direction
+	 *            The direction (
+	 *            {@link FileReleaseManager#FILE_RELEASE_DIRECTION_INCOMMING} or
+	 *            {@link FileReleaseManager#FILE_RELEASE_DIRECTION_OUTGOING})
+	 * @return A list with the {@link FileRelease}s
+	 */
+	public List<FileRelease> getReleases(String userName, String direction) {
+
+		Collection<FileRelease> releases = this.releaseMap.values();
+
+		if (direction.equalsIgnoreCase(FILE_RELEASE_DIRECTION_INCOMMING)) {
+			return extractIncommingReleases(userName, releases);
+		}
+
+		if (direction.equalsIgnoreCase(FILE_RELEASE_DIRECTION_OUTGOING)) {
+			return extractOutgoingReleases(userName, releases);
+		}
+
+		LOG.debug("unknown file request direction value '" + direction + "'. Returing empty file request list");
+		return Collections.emptyList();
+	}
+
+	/**
+	 * Returns all {@link FileRelease}s for a given user, in the given
+	 * direction, with the given state.
+	 * 
+	 * @param userName
+	 *            The user name.
+	 * @param direction
+	 *            The direction (
+	 *            {@link FileReleaseManager#FILE_RELEASE_DIRECTION_INCOMMING} or
+	 *            {@link FileReleaseManager#FILE_RELEASE_DIRECTION_OUTGOING})
+	 * @param state
+	 *            The state of the {@link FileRelease}s to return
+	 * @return A list with the {@link FileRelease}s
+	 */
+	public List<FileRelease> getReleases(String userName, String direction, String state) {
+		List<FileRelease> list = getReleases(userName, direction);
+		return extractReleasesByState(state, list);
+	}
+
+	/**
+	 * Adds a new {@link FileRelease}.
+	 * 
+	 * @param fileRelease
+	 *            The {@link FileRelease} to add.
+	 */
+	public void addFileRelease(FileRelease fileRelease) {
+		if (this.releaseMap.containsKey(fileRelease.identifier)) {
+			throw new FileReleaseException(
+					"FileRelease with identifier '" + fileRelease.identifier + "' already exists!");
+		}
+		this.releaseMap.put(fileRelease.identifier, fileRelease);
+	}
+
+	/**
+	 * 
+	 * Removes a {@link FileRelease}
+	 * 
+	 * @param fileRelease
+	 *            The {@link FileRelease} the remove.
+	 */
+	public void removeFileRelease(FileRelease fileRelease) {
+		if (!this.releaseMap.containsKey(fileRelease.identifier)) {
+			throw new FileReleaseException(
+					"FileRelease with identifier '" + fileRelease.identifier + "' does not exist!");
+		}
+		this.releaseMap.remove(fileRelease.identifier);
+	}
+
+	/**
+	 * 
+	 * Checks if the given file link can be deleted
+	 * 
+	 * @param fileLink
+	 *            The file link to check
+	 * @return TRUE if the file link can be deleted. Otherwise FALSE.
+	 */
+	public boolean canDeleteFileLink(String identfier, String fileLink) {
+
+		if (fileLink == null || fileLink.trim().length() <= 0) {
+			return false;
+		}
+
+		for (FileRelease fileRelease : this.releaseMap.values()) {
+
+			// skipping fileRelease to delete
+			if (fileRelease.identifier.equalsIgnoreCase(identfier)) {
+				continue;
+			}
+
+			if (fileRelease.fileLink.equalsIgnoreCase(fileLink)) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	@PostConstruct
+	private void onInitialize() {
+		this.releaseMap = this.releasePersistance.load();
+	}
+
+	@PreDestroy
+	private void onDestroy() {
+		this.releasePersistance.save(this.releaseMap);
+	}
+
+	private List<FileRelease> extractIncommingReleases(String receiverName, Collection<FileRelease> releases) {
+		List<FileRelease> incommingList = new ArrayList<>();
+		for (FileRelease fileRelease : releases) {
+			if (fileRelease.receiver.equalsIgnoreCase(receiverName)) {
+				updateExpired(fileRelease);
+				incommingList.add(fileRelease);
+			}
+		}
+		return incommingList;
+	}
+
+	private List<FileRelease> extractOutgoingReleases(String senderName, Collection<FileRelease> releases) {
+		List<FileRelease> outgoingList = new ArrayList<>();
+		for (FileRelease fileRelease : releases) {
+			if (fileRelease.sender.equalsIgnoreCase(senderName)) {
+				updateExpired(fileRelease);
+				outgoingList.add(fileRelease);
+			}
+		}
+		return outgoingList;
+	}
+
+	private List<FileRelease> extractReleasesByState(String state, List<FileRelease> list) {
+		List<FileRelease> statedList = new ArrayList<>();
+		for (FileRelease fileRelease : list) {
+			if (fileRelease.state.equalsIgnoreCase(state)) {
+				statedList.add(fileRelease);
+			}
+		}
+		return statedList;
+	}
+
+	private void updateExpired(FileRelease fileRelease) {
+		long current = System.currentTimeMillis();
+
+		if (!fileRelease.state.equalsIgnoreCase(FILE_RELEASE_STATE_RELEASED)) {
+			return;
+		}
+
+		if (fileRelease.expire <= current) {
+			fileRelease.state = FILE_RELEASE_STATE_EXPIRED;
+		}
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleasePersistance.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleasePersistance.java
new file mode 100644
index 0000000..bc7c41d
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/FileReleasePersistance.java
@@ -0,0 +1,128 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ejb.Stateless;
+
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+ * FileReleasePersistance bean implementation.
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Stateless
+public class FileReleasePersistance {
+
+	private static final Logger LOG = LoggerFactory.getLogger(FileReleasePersistance.class);
+
+	private static final String TARGET_FILE_NAME = "mdm_filerelease_storage.sav";
+
+	/**
+	 * Persists the given file release map.
+	 * 
+	 * @param map
+	 *            The map to persist.
+	 */
+	public void save(Map<String, FileRelease> map) {
+
+		String userHomePath = System.getProperty("user.home");
+		File directory = new File(userHomePath);
+
+		File targetFile = new File(directory, TARGET_FILE_NAME);
+		writeFile(targetFile, map);
+	}
+
+	/**
+	 * 
+	 * Loads the {@link FileRelease} into a map.
+	 * 
+	 * @return The map that contains the {@link FileRelease}s
+	 */
+	public Map<String, FileRelease> load() {
+
+		String userHomePath = System.getProperty("user.home");
+		File directory = new File(userHomePath);
+
+		File targetFile = new File(directory, TARGET_FILE_NAME);
+		
+		return loadFile(targetFile);
+	}
+
+	private void writeFile(File targetFile, Map<String, FileRelease> map) {
+
+		LOG.debug("Writing FileRelease storage file to '" + targetFile.getAbsolutePath() + "'");
+
+		try {
+			if (targetFile.exists()) {
+				deleteFile(targetFile);
+			}
+
+			try (ObjectOutputStream oos = new ObjectOutputStream(
+					new BufferedOutputStream(new FileOutputStream(targetFile)))) {
+				oos.writeObject(map);
+			}
+		} catch (IOException e) {
+			throw new FileReleaseException(e.getMessage(), e);
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	private Map<String, FileRelease> loadFile(File targetFile) {
+
+		LOG.debug("Loading FileRelease storage file from '" + targetFile.getAbsolutePath() + "'");
+
+		try {
+			if (!targetFile.exists()) {
+				LOG.warn("Storage file does not exist at '" + targetFile.getAbsolutePath()
+						+ "'. Using an empty FileRelease pool");
+				return new HashMap<String, FileRelease>();
+			}
+
+			try (ObjectInputStream ois = new ObjectInputStream(
+					new BufferedInputStream(new FileInputStream(targetFile)))) {
+				return (Map<String, FileRelease>) ois.readObject();
+			}
+		} catch (IOException e) {
+			throw new FileReleaseException(e.getMessage(), e);
+		} catch (ClassNotFoundException e) {
+			throw new FileReleaseException(e.getMessage(), e);
+		}
+	}
+
+	private void deleteFile(File targetFile) {
+		boolean deleted = targetFile.delete();
+		if (!deleted) {
+			throw new FileReleaseException(
+					"Unable to delete FileRelease storage file at '" + targetFile.getAbsolutePath() + "'");
+		}
+	}
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/AbstractFileConverter.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/AbstractFileConverter.java
new file mode 100644
index 0000000..abccf7d
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/AbstractFileConverter.java
@@ -0,0 +1,275 @@
+
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control.converter;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.eclipse.mdm.api.base.adapter.Attribute;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.base.query.Filter;
+import org.eclipse.mdm.api.base.search.SearchService;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.filerelease.control.FileReleaseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+ * Abstract implementation of {@link IFileConverter}. Provides some utility
+ * methods for further {@link IFileConverter} implementations.
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public abstract class AbstractFileConverter implements IFileConverter {
+
+	protected static final Logger LOG = LoggerFactory.getLogger(AbstractFileConverter.class);
+
+	/**
+	 * 
+	 * Creates a zip file from the given folder.
+	 * 
+	 * @param targetFile
+	 *            The path to the zip file to create
+	 * @param folderToZip
+	 *            The path to the folder to zip.
+	 * @throws FileConverterException
+	 *             Thrown if an error occurs.
+	 */
+	protected void zipFolder(String targetFile, String folderToZip) throws FileConverterException {
+		zipFolder(targetFile, folderToZip, false);
+	}
+
+	/**
+	 * Creates a zip file from the given folder.
+	 * 
+	 * @param targetFile
+	 *            The path to the zip file to create
+	 * @param folderToZip
+	 *            The path to the folder to zip.
+	 * @param overWrite
+	 *            True if the target file should be overwritten.
+	 * @throws FileConverterException
+	 *             Thrown if an error occurs.
+	 */
+	protected void zipFolder(String targetFile, String folderToZip, boolean overWrite) throws FileConverterException {
+		File source = new File(folderToZip);
+		File target = new File(targetFile);
+		if (!source.exists()) {
+			throw new FileConverterException("Unable to zip folder: " + folderToZip + ". The folder does not exist.");
+		}
+		if (!overWrite && target.exists()) {
+			throw new FileConverterException(
+					"Unable to zip folder: " + folderToZip + ". The target file" + targetFile + "already exists.");
+		}
+		if (overWrite && target.exists()) {
+			target.delete();
+		}
+		zipFiles(listAllFilesRecursive(source), target, folderToZip);
+	}
+
+	/**
+	 * 
+	 * Locates the attribute value for the given string attribute.
+	 * 
+	 * @param em
+	 *            The entity manager that manages the attribute
+	 * @param testStep
+	 *            The {@link TestStep}
+	 * @param entityName
+	 *            The name of the entity that belongs to the attribute.
+	 * @param attributeName
+	 *            The name of the attribute.
+	 * @return The attribute value.
+	 */
+	protected String locateStringAttributeValue(ApplicationContext context, TestStep testStep, String entityName,
+			String attributeName) {
+		try {
+			SearchService searchService = context.getSearchService()
+					.orElseThrow(() -> new FileReleaseException("Mandatory MDM SearchService not found"));
+			
+			List<EntityType> list = searchService.listEntityTypes(TestStep.class);
+
+			EntityType entityType = locateEntityType(list, entityName);
+			Attribute attribute = locateAttribute(entityType, attributeName);
+
+			EntityType testStepET = locateEntityType(list, TestStep.class.getSimpleName());
+			Filter idFilter = Filter.idOnly(testStepET, testStep.getID());
+
+			List<TestStep> results = searchService.fetch(TestStep.class, Collections.singletonList(attribute),
+					idFilter);
+
+			if (results.size() < 0 || results.size() > 1) {
+				throw new FileReleaseException(
+						"Illegal search result for attribute value from '" + entityName + "." + attributeName + "'");
+			}
+
+			TestStep resultTestStep = results.get(0);
+			return resultTestStep.getValue(attributeName).extract();
+		} catch (DataAccessException e) {
+			throw new FileReleaseException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * Creates a new directory
+	 * 
+	 * @param path
+	 *            The path to the directory to create
+	 * @return The created directory {@link File}
+	 */
+	protected File createDirectory(String path) {
+		File directory = new File(path);
+		if (!directory.exists() && !directory.mkdir()) {
+			throw new FileReleaseException("Unable to create directory at '" + directory.getAbsolutePath() + "'");
+		}
+
+		return directory;
+	}
+
+	/**
+	 * Deletes the given directory
+	 * 
+	 * @param directory
+	 *            The directory to delete.
+	 */
+	protected void deleteDirectory(File directory) {
+		if (!directory.exists()) {
+			return;
+		}
+
+		File[] files = directory.listFiles();
+		for (File file : files) {
+			if (file.isDirectory()) {
+				deleteDirectory(file);
+			}
+			if (!file.delete()) {
+				LOG.warn("Unable to delete file at '" + file.getAbsolutePath() + "'");
+			}
+		}
+		if (!directory.delete()) {
+			LOG.warn("Unable to delete directory at '" + directory.getAbsolutePath() + "'");
+		}
+	}
+
+	/**
+	 * Locates the directory {@link File} for the given path
+	 * 
+	 * @param inputPath
+	 *            The path to the directory.
+	 * @return The {@link File}
+	 */
+	protected File locateInputDirectory(String inputPath) {
+		File pakInputDirectory = new File(inputPath);
+		if (!pakInputDirectory.exists()) {
+			throw new FileReleaseException(
+					"Input path at '" + pakInputDirectory.getAbsolutePath() + "' does not exist!");
+		}
+
+		if (!pakInputDirectory.isDirectory()) {
+			throw new FileReleaseException(
+					"Input path at '" + pakInputDirectory.getAbsolutePath() + "' is not a directory path!");
+		}
+
+		return pakInputDirectory;
+	}
+
+	protected String readPropertyValue(String propertyValue, boolean mandatory, String defaultValue,
+			String propertyName) throws FileConverterException {
+		if (propertyValue == null || propertyValue.trim().length() <= 0) {
+			if (mandatory) {
+				throw new FileConverterException("Mandatory property with name '" + propertyName + "' is not defined!");
+			}
+			return defaultValue;
+		}
+		return propertyValue;
+	}
+
+	private void zipFiles(List<File> list, File target, String sourcePath) throws FileConverterException {
+		try (ZipOutputStream zipStream = new ZipOutputStream(new FileOutputStream(target))) {
+			for (File file : list) {
+				zipFile(file, zipStream, sourcePath);
+			}
+		} catch (IOException ioe) {
+			throw new FileConverterException(
+					"An error occured when creating an zip archive from the folder: " + target.getAbsolutePath(), ioe);
+		}
+	}
+
+	private List<File> listAllFilesRecursive(File sourceFolder) {
+		List<File> files = new ArrayList<>();
+		File[] subFiles = sourceFolder.listFiles();
+		for (File f : subFiles) {
+			if (f.isDirectory()) {
+				files.addAll(listAllFilesRecursive(f));
+			} else {
+				files.add(f);
+			}
+		}
+		return files;
+	}
+
+	private void zipFile(File file, ZipOutputStream zipStream, String sourcePath) throws FileConverterException {
+
+		try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) {
+			String entryName = file.getAbsolutePath().replace(sourcePath, "");
+
+			ZipEntry zipEntry = new ZipEntry(entryName);
+			zipStream.putNextEntry(zipEntry);
+			byte[] buffer = new byte[1024];
+
+			for (int len = in.read(buffer); len > 0; len = in.read(buffer)) {
+				zipStream.write(buffer, 0, len);
+			}
+		} catch (IOException ioe) {
+			throw new FileConverterException("An error occured when zipping the file: " + file.getAbsolutePath(), ioe);
+		}
+
+	}
+
+	private EntityType locateEntityType(List<EntityType> list, String entityName) {
+		for (EntityType entityType : list) {
+			if (entityType.getName().equals(entityName)) {
+				return entityType;
+			}
+		}
+		throw new FileReleaseException("Entity with name '" + entityName + "' not available for TestStep query");
+	}
+
+	private Attribute locateAttribute(EntityType entityType, String attributeName) {
+		List<Attribute> list = entityType.getAttributes();
+		for (Attribute attribute : list) {
+			if (attribute.getName().equals(attributeName)) {
+				return attribute;
+			}
+		}
+		throw new FileReleaseException(
+				"Attribute with name '" + attributeName + "' does not exist at entity '" + entityType.getName() + "'");
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/FileConverterException.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/FileConverterException.java
new file mode 100644
index 0000000..3a3553c
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/FileConverterException.java
@@ -0,0 +1,50 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control.converter;
+
+/**
+ * FileConverterException
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class FileConverterException extends Exception {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param message
+	 *            The error message
+	 */
+	public FileConverterException(String message) {
+		super(message);
+	}
+
+	/**
+	 * Constructor#
+	 * 
+	 * @param message
+	 *            The error message
+	 * @param t
+	 *            The {@link Throwable} that caused the exception.
+	 */
+	public FileConverterException(String message, Throwable t) {
+		super(message, t);
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/FileConverterPAK2ATFX.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/FileConverterPAK2ATFX.java
new file mode 100644
index 0000000..b542102
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/FileConverterPAK2ATFX.java
@@ -0,0 +1,176 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control.converter;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.filerelease.control.FileReleaseException;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.eclipse.mdm.property.GlobalProperty;
+
+/**
+ * 
+ * {@link IFileConverter} implementation for converting PAK to ATFX
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@RequestScoped
+public class FileConverterPAK2ATFX extends AbstractFileConverter {
+
+	private static final String CONVERTER_NAME = "PAK2ATFX";
+	private static final String COMPONENT_CONFIG_ROOT_FOLDER = "org.eclipse.mdm.filerelease";
+
+	private static final String MODEL_FILE_NAME = "model.atfx.technical";
+	private static final String OUTPUT_SUB_TEMP_DIRECTORY = "tmp";
+	private static final String ATFX_OUTPUT_FILE_NAME_PREFIX = "_ATFX.zip";
+
+	@Inject
+	@GlobalProperty("filerelease.converter.pak.pakApplicationPath")
+	private String pakApplicationPath = "";
+	@Inject
+	@GlobalProperty("filerelease.converter.pak.modelTypeEntity")
+	private String modelTypeEntity = "";
+	@Inject
+	@GlobalProperty("filerelease.converter.pak.modelTypeAttribute")
+	private String modelTypeAttribute = "";
+	@Inject
+	@GlobalProperty("filerelease.converter.pak.pakInputEntity")
+	private String pakInputEntity = "";
+	@Inject
+	@GlobalProperty("filerelease.converter.pak.pakInputAttribute")
+	private String pakInputAttribute = "";
+
+	@Override
+	public void execute(FileRelease fileRelease, TestStep testStep, ApplicationContext context, File targetDirectory)
+			throws FileConverterException {
+
+		int returnValue = -1;
+
+		String pakApplicationPathValue = super.readPropertyValue(this.pakApplicationPath, true, null,
+				"pakApplicationPath");
+		String modelTypeEntityValue = super.readPropertyValue(this.modelTypeEntity, true, null, "modelTypeEntity");
+		String modelTypeAttributeValue = super.readPropertyValue(this.modelTypeAttribute, true, null,
+				"modelTypeAttribute");
+		String pakInputEntityValue = super.readPropertyValue(this.pakInputEntity, true, null, "pakInputEntity");
+		String pakInputAttributeValue = super.readPropertyValue(this.pakInputAttribute, true, null,
+				"pakInputAttribute");
+
+		String modelType = locateStringAttributeValue(context, testStep, modelTypeEntityValue, modelTypeAttributeValue);
+		String inputPath = locateStringAttributeValue(context, testStep, pakInputEntityValue, pakInputAttributeValue);
+
+		File pakApplicationFile = locatePakApplicationFile(pakApplicationPathValue);
+		File modelFile = locateModelFileForModelType(modelType);
+		File inputDirectory = locateInputDirectory(inputPath);
+		File outputDirectory = createDirectory(targetDirectory.getAbsolutePath() + File.separator + fileRelease.name);
+		File outputTempDirectory = createDirectory(
+				outputDirectory.getAbsolutePath() + File.separator + OUTPUT_SUB_TEMP_DIRECTORY);
+		File outputZIPFile = new File(outputDirectory, fileRelease.name + ATFX_OUTPUT_FILE_NAME_PREFIX);
+
+		try {
+			if (!outputZIPFile.exists()) {
+				LOG.debug("executing external pak application ...");
+				returnValue = createATFX(pakApplicationFile, inputDirectory, outputTempDirectory, modelFile);
+				if (returnValue != 0) {
+					String errorMessage = translateErrorCode(returnValue);
+					throw new FileConverterException(
+							"external pak application ends with an error (message: '" + errorMessage + "')");
+				}
+				LOG.debug("executing external pak application ... done!");
+
+				LOG.debug("executing zip process for pak application result ...");
+
+				zipFolder(outputZIPFile.getAbsolutePath(), outputTempDirectory.getAbsolutePath(), true);
+				LOG.debug("executing zip process for pak application result ... done");
+			}
+
+			fileRelease.fileLink = fileRelease.name + File.separator + outputZIPFile.getName();
+
+		} catch (IOException | InterruptedException e) {
+			throw new FileConverterException(e.getMessage(), e);
+		} finally {
+			deleteDirectory(outputTempDirectory);
+		}
+	}
+
+	@Override
+	public String getConverterName() {
+		return CONVERTER_NAME;
+	}
+
+	private int createATFX(File pakApplicationFile, File inputDirectory, File outputTempDirectory, File modelFile)
+			throws IOException, InterruptedException {
+
+		ProcessBuilder pb = new ProcessBuilder(pakApplicationFile.getAbsolutePath(), "-M",
+				inputDirectory.getAbsolutePath(), "-E", outputTempDirectory.getAbsolutePath(), "-MF",
+				modelFile.getAbsolutePath());
+		Process process = pb.start();
+
+		return process.waitFor();
+	}
+
+	private String translateErrorCode(int returnValue) {
+
+		String errorMessage;
+
+		if (returnValue == 1) {
+			errorMessage = "invalid input pak measurement";
+		} else if (returnValue == 2) {
+			errorMessage = "illegal command (a parameter is missing)";
+		} else if (returnValue == 6) {
+			errorMessage = "foreign model measurement";
+		} else if (returnValue == 7) {
+			errorMessage = "illegal input path format";
+		} else if (returnValue == 8) {
+			errorMessage = "source directory does not exist";
+		} else if (returnValue == 9) {
+			errorMessage = "target directory does not exist";
+		} else if (returnValue == 10) {
+			errorMessage = "target directory is not empty";
+		} else if (returnValue == 11) {
+			errorMessage = "illegal commant (model file parameter is missing)";
+		} else {
+			errorMessage = "unknown error with error code '" + returnValue + "'";
+		}
+		return errorMessage + "  (errorCode = '" + returnValue + "')";
+	}
+
+	private File locatePakApplicationFile(String pakApplicationFilePath) {
+		File pakApplicationFile = new File(pakApplicationFilePath);
+		if (!pakApplicationFile.exists()) {
+			throw new FileReleaseException("pak application executable file at '" + pakApplicationFile.getAbsolutePath()
+					+ "' does not exist!");
+		}
+		return pakApplicationFile;
+	}
+
+	private File locateModelFileForModelType(String modelType) {
+		File modelFile = new File(
+				COMPONENT_CONFIG_ROOT_FOLDER + File.separator + modelType + File.separator + MODEL_FILE_NAME);
+		if (!modelFile.exists()) {
+			throw new FileReleaseException("mapping file for atfx model with type '" + modelType
+					+ "' does not exist! (expected at: '" + modelFile.getAbsolutePath() + "')");
+		}
+		return modelFile;
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/FileConverterPAK2RAW.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/FileConverterPAK2RAW.java
new file mode 100644
index 0000000..fab2d07
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/FileConverterPAK2RAW.java
@@ -0,0 +1,75 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control.converter;
+
+import java.io.File;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.eclipse.mdm.property.GlobalProperty;
+
+/**
+ * 
+ * {@link IFileConverter} implementation for converting PAK to RAW
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@RequestScoped
+public class FileConverterPAK2RAW extends AbstractFileConverter {
+
+	private static final String ATFX_OUTPUT_FILE_NAME_PREFIX = "_RAW.zip";
+
+	@Inject
+	@GlobalProperty("filerelease.converter.raw.pakInputEntity")
+	private String pakInputEntity = "";
+	@Inject
+	@GlobalProperty("filerelease.converter.raw.pakInputAttribute")
+	private String pakInputAttribute = "";
+
+	@Override
+	public void execute(FileRelease fileRelease, TestStep testStep, ApplicationContext context, File targetDirectory)
+			throws FileConverterException {
+
+		String pakInputEntityValue = super.readPropertyValue(this.pakInputEntity, true, null, "pakInputEntity");
+		String pakInputAttributeValue = super.readPropertyValue(this.pakInputAttribute, true, null,
+				"pakInputAttribute");
+
+		String inputPath = locateStringAttributeValue(context, testStep, pakInputEntityValue, pakInputAttributeValue);
+		File inputDirectory = locateInputDirectory(inputPath);
+
+		File outputDirectory = createDirectory(targetDirectory.getAbsolutePath() + File.separator + fileRelease.name);
+		File outputZIPFile = new File(outputDirectory, fileRelease.name + ATFX_OUTPUT_FILE_NAME_PREFIX);
+
+		if (!outputZIPFile.exists()) {
+			LOG.debug("executing zip process for pak raw data ...");
+			zipFolder(outputZIPFile.getAbsolutePath(), inputDirectory.getAbsolutePath(), true);
+			LOG.debug("executing zip process for pak raw data ... done");
+		}
+
+		fileRelease.fileLink = fileRelease.name + File.separator + outputZIPFile.getName();
+	}
+
+	@Override
+	public String getConverterName() {
+		return "PAK2RAW ";
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/IFileConverter.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/IFileConverter.java
new file mode 100644
index 0000000..90fd179
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/control/converter/IFileConverter.java
@@ -0,0 +1,55 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.control.converter;
+
+import java.io.File;
+
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+
+/**
+ * 
+ * Interface for file converters
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public interface IFileConverter {
+
+	/**
+	 * 
+	 * Executes the file conversion
+	 * 
+	 * @param fileRelease
+	 *            The file release that contains the file to convert
+	 * @param testStep
+	 *            The {@link TestStep}
+	 * @param context
+	 *            The {@link ApplicationContext}
+	 * @throws FileConverterException
+	 *             Thrown if the file conversion fails.
+	 */
+	void execute(FileRelease fileRelease, TestStep testStep, ApplicationContext context, File targetDirectory)
+			throws FileConverterException;
+
+	/**
+	 * Returns the name of the file converter
+	 * 
+	 * @return The name of the file converter
+	 */
+	String getConverterName();
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/entity/FileRelease.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/entity/FileRelease.java
new file mode 100644
index 0000000..d40bc3c
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/entity/FileRelease.java
@@ -0,0 +1,82 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.entity;
+
+import java.io.Serializable;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.TestStep;
+
+/**
+ * FileRelease entity
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@XmlRootElement
+public class FileRelease implements Serializable {
+
+	private static final long serialVersionUID = -9016111258701009299L;
+
+	/** unique identifier of a {@link FileRelease} */
+	public String identifier = "";
+	/** current state of the {@link FileRelease} */
+	public String state = "";
+
+	/** name of the source {@link TestStep} */
+	public String name = "";
+	/**
+	 * name of the source {@link Environment} of the business object (part of
+	 * MDM URI)
+	 */
+	public String sourceName = "";
+	/** name of the type of the source business object (part of MDM URI) */
+	public String typeName = "";
+	/** id of the source business object (part of URI) */
+	public String id = "";
+
+	/** release sender name (MDM user name) */
+	public String sender = "";
+	/** release receiver name (MDM user name) */
+	public String receiver = "";
+
+	/**
+	 * order message to specify by the sender on creating a {@link FileRelease}
+	 */
+	public String orderMessage = "";
+	/**
+	 * reject message to specify by the receiver on rejecting a
+	 * {@link FileRelease}
+	 */
+	public String rejectMessage = "";
+	/**
+	 * system error message set by the converter system if an error occuring
+	 * during generating the target file
+	 */
+	public String errorMessage = "";
+
+	/** output data format of the generated file */
+	public String format = "";
+	/** relative file link of the generated file */
+	public String fileLink = "";
+
+	/** number of days */
+	public int validity = 0;
+	/** calculated expire date (time stamp) */
+	public long expire = 0;
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/entity/FileReleaseResponse.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/entity/FileReleaseResponse.java
new file mode 100644
index 0000000..e000a34
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/entity/FileReleaseResponse.java
@@ -0,0 +1,63 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.entity;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 
+ * {@link FileReleaseResponse}
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class FileReleaseResponse {
+
+	public List<FileRelease> data;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param fileRelease
+	 *            {@link FileRelease}
+	 */
+	public FileReleaseResponse(FileRelease fileRelease) {
+		this.data = new ArrayList<FileRelease>();
+		this.data.add(fileRelease);
+	}
+
+	/**
+	 * Constructor
+	 * 
+	 * @param list
+	 *            list of {@link FileRelease}s
+	 */
+	public FileReleaseResponse(List<FileRelease> list) {
+		this.data = new ArrayList<FileRelease>();
+		this.data.addAll(list);
+	}
+
+	/**
+	 * returns the {@link FileRelease} data
+	 * 
+	 * @return the {@link FileRelease} data
+	 */
+	public List<FileRelease> getData() {
+		return Collections.unmodifiableList(this.data);
+	}
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/utils/FileReleasePermissionUtils.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/utils/FileReleasePermissionUtils.java
new file mode 100644
index 0000000..8786998
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/utils/FileReleasePermissionUtils.java
@@ -0,0 +1,157 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.utils;
+
+import java.util.List;
+
+import org.eclipse.mdm.filerelease.control.FileReleaseException;
+import org.eclipse.mdm.filerelease.control.FileReleaseManager;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+
+/**
+ * Provides utility methods for checking permissions on {@link FileRelease}s
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public final class FileReleasePermissionUtils {
+	
+	private FileReleasePermissionUtils() {
+	}
+
+	/**
+	 * Checks if the user with the given name has the permission to create the
+	 * given {@link FileRelease}s.
+	 * 
+	 * @param request
+	 *            The {@link FileReleaseRequest}
+	 * @param userName
+	 *            The name of the user to check the permissions
+	 * @param list
+	 *            The list with the {@link FileRelease}
+	 * @throws FileReleaseException
+	 *             if the user is not allowed to create the {@link FileRelease}s
+	 * 
+	 */
+	public static void canCreate(FileRelease newFileRelease, String userName, List<FileRelease> list) {
+		for (FileRelease fileRelease : list) {
+
+			boolean sourceNameEqual = fileRelease.sourceName.equals(newFileRelease.sourceName);
+			boolean typeNameEqual = fileRelease.typeName.equals(newFileRelease.typeName);
+			boolean idEqual = fileRelease.id == newFileRelease.id;
+			boolean formatEqual = fileRelease.format.equals(newFileRelease.format);
+
+			if (sourceNameEqual && typeNameEqual && idEqual && formatEqual) {
+				throw new FileReleaseException("FileRelease for user '" + userName + "' and path'"
+						+ fileRelease.sourceName + "/" + fileRelease.typeName + "/" + fileRelease.id + "' (Format: '"
+						+ fileRelease.format + "') already exists!");
+			}
+		}
+
+	}
+
+	/**
+	 * Checks if the user with the given name has the permission to delete the
+	 * given {@link FileRelease}
+	 * 
+	 * @param fileRelease
+	 *            The {@link FileRelease} to check
+	 * @param userName
+	 *            The name of the user to check the permissions
+	 * @throws FileReleaseException
+	 *             if the user is not allowed to delete the {@link FileRelease}
+	 */
+	public static void canDelete(FileRelease fileRelease, String userName) {
+		if (!fileRelease.sender.equalsIgnoreCase(userName)) {
+			throw new FileReleaseException("user with name '" + userName + "' can't remove FileRelease with id '"
+					+ fileRelease.identifier + "'");
+		}
+
+		if (fileRelease.state.equalsIgnoreCase(FileReleaseManager.FILE_RELEASE_STATE_PROGRESSING)) {
+			throw new FileReleaseException("unable to remove FileRelease in state '"
+					+ FileReleaseManager.FILE_RELEASE_STATE_PROGRESSING + "'");
+		}
+	}
+
+	/**
+	 * Checks if the user with the given name has the permission to approve the
+	 * given {@link FileRelease}
+	 * 
+	 * @param fileRelease
+	 *            The {@link FileRelease} to check
+	 * @param userName
+	 *            The name of the user to check the permissions
+	 * @throws FileReleaseException
+	 *             if the user is not allowed to approve the {@link FileRelease}
+	 */
+	public static void canApprove(FileRelease fileRelease, String userName) {
+		if (!fileRelease.receiver.equalsIgnoreCase(userName)) {
+			throw new FileReleaseException("user with name '" + userName + "' can't approve FileRelease with id '"
+					+ fileRelease.identifier + "'");
+		}
+
+		if (!fileRelease.state.equalsIgnoreCase(FileReleaseManager.FILE_RELEASE_STATE_ORDERED)) {
+			throw new FileReleaseException("unable to approve FileRelease in state '" + fileRelease.state + "'");
+		}
+	}
+
+	/**
+	 * Checks if the user with the given name has the permission to release the
+	 * given {@link FileRelease}
+	 * 
+	 * @param fileRelease
+	 *            The {@link FileRelease} to check
+	 * @param userName
+	 *            The name of the user to check the permissions
+	 * @throws FileReleaseException
+	 *             if the user is not allowed to approve the {@link FileRelease}
+	 */
+	public static void canRelease(FileRelease fileRelease, String userName) {
+		if (!fileRelease.receiver.equalsIgnoreCase(userName)) {
+			throw new FileReleaseException("user with name '" + userName + "' can't release FileRelease with id '"
+					+ fileRelease.identifier + "'");
+		}
+
+		if (!fileRelease.state.equalsIgnoreCase(FileReleaseManager.FILE_RELEASE_STATE_APPROVED)) {
+			throw new FileReleaseException("unable to release FileRelease in state '" + fileRelease.state + "'");
+		}
+	}
+
+	/**
+	 * Checks if the user with the given name is allowed to reject the given
+	 * {@link FileRelease}
+	 * 
+	 * @param fileRelease
+	 *            The {@link FileRelease} to reject
+	 * @param userName
+	 *            The name of the user to check the permissions
+	 * @throws FileReleaseException
+	 *             if the user is not allowed to reject the {@link FileRelease}
+	 */
+	public static void canReject(FileRelease fileRelease, String userName) {
+
+		if (!fileRelease.receiver.equalsIgnoreCase(userName)) {
+			throw new FileReleaseException("user with name '" + userName + "' can't reject FileRelease with id '"
+					+ fileRelease.identifier + "'");
+		}
+
+		if (!fileRelease.state.equalsIgnoreCase(FileReleaseManager.FILE_RELEASE_STATE_ORDERED)) {
+			throw new FileReleaseException("unable to reject FileRelease in state '" + fileRelease.state + "'");
+		}
+
+	}
+
+}
diff --git a/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/utils/FileReleaseUtils.java b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/utils/FileReleaseUtils.java
new file mode 100644
index 0000000..63af8c4
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/main/java/org/eclipse/mdm/filerelease/utils/FileReleaseUtils.java
@@ -0,0 +1,247 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease.utils;
+
+import org.eclipse.mdm.api.base.model.Test;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.model.User;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.eclipse.mdm.filerelease.control.FileReleaseException;
+import org.eclipse.mdm.filerelease.control.FileReleaseManager;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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 java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * 
+ * Provides utility methods
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public final class FileReleaseUtils {
+
+	private static final Logger LOG = LoggerFactory.getLogger(FileReleaseUtils.class);
+	
+	private FileReleaseUtils() {
+	}
+
+	/**
+	 * Returns the {@link User} that is logged in on the given
+	 * {@link ConnectorService}
+	 * 
+	 * @param connectorService
+	 *            The {@link ConnectorService}
+	 * @return The {@link User}
+	 */
+	public static User getLoggedOnUser(ConnectorService connectorService) {
+
+		try {
+			List<ApplicationContext> contextList = connectorService.getContexts();
+					
+			if (contextList == null || contextList.size() <= 0) {
+				throw new FileReleaseException("unable to locate neccessary EntityManager for file release service");
+			}
+
+			return extractUser(contextList.get(0)
+					.getEntityManager()
+					.map(em -> em.loadLoggedOnUser())
+					.orElseThrow(() -> new FileReleaseException("Entity manager not present!")));
+		} catch (DataAccessException e) {
+			throw new FileReleaseException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * Returns the {@link User} that is responsible for the given
+	 * {@link TestStep}
+	 * 
+	 * @param connectorService
+	 *            The {@link ConnectorService}
+	 * @param testStep
+	 *            The {@link TestStep}
+	 * @return The responsible {@link User}
+	 */
+	public static User getResponsiblePerson(ConnectorService connectorService, TestStep testStep) {
+		EntityManager em = connectorService.getContextByName(testStep.getSourceName())
+				.getEntityManager()
+				.orElseThrow(() -> new FileReleaseException("Entity manager not present!"));
+		Test test = getTestParent(em, testStep);
+		return extractUser(test.getResponsiblePerson());
+	}
+
+	/**
+	 * 
+	 * Loads the {@link TestStep} with the given URI from the given
+	 * {@link ConnectorService}
+	 * 
+	 * @param connectorService
+	 *            The {@link ConnectorService}
+	 * @param sourceName
+	 *            The source name
+	 * @param id
+	 *            The id of the {@link TestStep}
+	 * @return The loaded {@link TestStep}
+	 */
+	public static TestStep loadTestStep(ConnectorService connectorService, String sourceName, String id) {
+		try {
+			EntityManager em = connectorService.getContextByName(sourceName)
+					.getEntityManager()
+					.orElseThrow(() -> new FileReleaseException("Entity manager not present!"));
+			return em.load(TestStep.class, id);
+		} catch (DataAccessException e) {
+			throw new FileReleaseException(e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * 
+	 * Creates a {@link Response} with the given status.
+	 * 
+	 * @param response
+	 *            The object that should be returned in the response.
+	 * @param status
+	 *            The status of the response.
+	 * @return The {@link Response}
+	 */
+	public static Response toResponse(Object response, Status status) {
+		GenericEntity<Object> genEntity = new GenericEntity<Object>(response, response.getClass());
+		return Response.status(status).entity(genEntity).type(MediaType.APPLICATION_JSON).build();
+	}
+
+	/**
+	 * Deletes a filelink
+	 * 
+	 * @param file
+	 *            The file to delete
+	 */
+	public static void deleteFileLink(File file) {
+
+		if (!file.exists() || file.isDirectory()) {
+			LOG.warn("unable to delete file '" + file.getAbsolutePath() + "' (file does not exist)!");
+			return;
+		}
+
+		boolean deleted = file.delete();
+		if (!deleted) {
+			LOG.warn("unable to delete file '" + file.getAbsolutePath() + "' (unkown error)!");
+		}
+	}
+
+	/**
+	 * Checks if the given format is a valid file converter format.
+	 * 
+	 * @param format
+	 *            The format as string
+	 * @return TRUE if the format is valid. Otherwise FALSE.
+	 */
+	public static boolean isFormatValid(String format) {
+		boolean valid = false;
+
+		if (FileReleaseManager.CONVERTER_FORMAT_PAK2RAW.equalsIgnoreCase(format)) {
+			valid = true;
+		}
+
+		if (FileReleaseManager.CONVERTER_FORMAT_PAK2ATFX.equalsIgnoreCase(format)) {
+			valid = true;
+		}
+
+		return valid;
+	}
+
+	public static List<FileRelease> filterByConnectedSources(List<FileRelease> fileReleases,
+			ConnectorService connectorService) {
+
+		List<FileRelease> filteredList = new ArrayList<FileRelease>();
+
+		List<String> sourceNameList = listConnectedSourceNames(connectorService);
+		for (FileRelease fileRelease : fileReleases) {
+			if (isFileReleaseSourceConnected(fileRelease, sourceNameList)) {
+				filteredList.add(fileRelease);
+			}
+		}
+		return filteredList;
+
+	}
+
+	public static File locateTargetDirectory(String targetDirectoryPath) {
+		if (targetDirectoryPath == null || targetDirectoryPath.trim().length() <= 0) {
+			throw new FileReleaseException("mandatory targetDirectoryPath property is missing");
+		}
+		File targetDirectory = new File(targetDirectoryPath);
+		if (!targetDirectory.exists()) {
+			throw new FileReleaseException(
+					"configured target directory at '" + targetDirectory.getAbsolutePath() + "' does not exist!");
+		}
+		return targetDirectory;
+	}
+
+	private static boolean isFileReleaseSourceConnected(FileRelease fileRelease, List<String> sourceNameList) {
+		for (String sourceName : sourceNameList) {
+			if (fileRelease.sourceName != null && fileRelease.sourceName.equals(sourceName)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private static List<String> listConnectedSourceNames(ConnectorService connectorService) {
+		try {
+			return connectorService.getContexts()
+					.stream()
+					.map(c -> c.getEntityManager())
+					.filter(Optional::isPresent)
+				    .map(Optional::get)
+				    .map(em -> em.loadEnvironment().getSourceName())
+					.collect(Collectors.toList());
+		} catch (DataAccessException e) {
+			throw new FileReleaseException(e.getMessage(), e);
+		}
+	}
+
+	private static User extractUser(Optional<User> oUser) {
+		if (!oUser.isPresent()) {
+			throw new FileReleaseException("unable to locate neccessary User for file release service");
+		}
+		return oUser.get();
+	}
+
+	private static Test getTestParent(EntityManager em, TestStep testStep) {
+		try {
+			Optional<Test> oTest = em.loadParent(testStep, TestStep.PARENT_TYPE_TEST);
+			if (!oTest.isPresent()) {
+				throw new FileReleaseException(
+						"unable to locate Test parent for TestStep with ID '" + testStep.getID() + "'");
+			}
+			return oTest.get();
+		} catch (DataAccessException e) {
+			throw new FileReleaseException(e.getMessage(), e);
+		}
+	}
+}
diff --git a/org.eclipse.mdm.filerelease/src/test/java/org/eclipse/mdm/filerelease/FileReleaseServiceMockHelper.java b/org.eclipse.mdm.filerelease/src/test/java/org/eclipse/mdm/filerelease/FileReleaseServiceMockHelper.java
new file mode 100644
index 0000000..59567d2
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/test/java/org/eclipse/mdm/filerelease/FileReleaseServiceMockHelper.java
@@ -0,0 +1,166 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+
+import org.eclipse.mdm.api.base.adapter.Core;
+import org.eclipse.mdm.api.base.adapter.EntityStore;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.base.model.Test;
+import org.eclipse.mdm.api.base.model.TestStep;
+import org.eclipse.mdm.api.base.model.User;
+import org.eclipse.mdm.api.base.model.Value;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.eclipse.mdm.filerelease.control.FileConvertJobManager;
+import org.eclipse.mdm.filerelease.control.FileReleaseManager;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.mockito.Mockito;
+
+public class FileReleaseServiceMockHelper {
+
+	public static final String TEST_USERNAME_SELF = "sa";
+	public static final String TEST_USERNAME_OTHER = "ot";
+
+	public static final String TESTSTEP = "TestStep";
+
+	public static final int NUM_FILE_REL_IN = 3;
+	public static final int NUM_FILE_REL_OUT = 5;
+
+	public static final String ID_IN_PREFIX = "IN";
+	public static final String ID_OUT_PREFIX = "OUT";
+	
+	private FileReleaseServiceMockHelper() {
+	}
+
+	public static FileReleaseManager createFileReleaseManagerMock() throws Exception {
+
+		FileReleaseManager frManagerMock = new FileReleaseManager();
+		Field releaseMapField = frManagerMock.getClass().getDeclaredField("releaseMap");
+		releaseMapField.setAccessible(true);
+		releaseMapField.set(frManagerMock, new HashMap<>());
+		for (FileRelease fr : createFileReleaseMockList()) {
+			frManagerMock.addFileRelease(fr);
+		}
+
+		releaseMapField.setAccessible(false);
+		return frManagerMock;
+	}
+
+	public static FileConvertJobManager createFileConvertJobManagerMock() {
+		return Mockito.mock(FileConvertJobManager.class);
+	}
+
+	public static ConnectorService createConnectorServiceMock() throws Exception {
+		ConnectorService mockedConnectorService = Mockito.mock(ConnectorService.class);
+		ApplicationContext mockedContext = createContextMock();
+		List<ApplicationContext> contextList = new ArrayList<>();
+		contextList.add(mockedContext);
+		when(mockedConnectorService.getContexts()).thenReturn(contextList);
+		when(mockedConnectorService.getContextByName("MDMENV")).thenReturn(contextList.get(0));
+		return mockedConnectorService;
+	}
+
+	public static ApplicationContext createContextMock() throws Exception {
+		EntityManager em = Mockito.mock(EntityManager.class);
+
+		Environment mockedEnv = createEntityMock(Environment.class, "MDMENV", "MDMENV", "1");
+		when(em.loadEnvironment()).thenReturn(mockedEnv);
+
+		User mockedUser = createEntityMock(User.class, TEST_USERNAME_SELF, "MDMENV", "1");
+		when(em.loadLoggedOnUser()).thenReturn(Optional.of(mockedUser));
+
+		Test mockedTest = createEntityMock(Test.class, "Test", "MDMENV", "1");
+		mockedTest.setResponsiblePerson(mockedUser);
+		when(em.loadParent(any(TestStep.class), eq(TestStep.PARENT_TYPE_TEST)))
+				.thenReturn(Optional.of(mockedTest));
+
+		TestStep mockedTestStep1 = createEntityMock(TestStep.class, "Teststep", "MDMENV", "123");
+		when(em.load(TestStep.class, "123")).thenReturn(mockedTestStep1);
+
+		TestStep mockedTestStep2 = createEntityMock(TestStep.class, "Teststep", "MDMENV", "1234");
+		when(em.load(TestStep.class, "1234")).thenReturn(mockedTestStep2);
+
+		ApplicationContext context = Mockito.mock(ApplicationContext.class);
+		when(context.getEntityManager()).thenReturn(Optional.of(em));
+		return context;
+	}
+
+	private static List<FileRelease> createFileReleaseMockList() {
+		List<FileRelease> fileReleasMockList = new ArrayList<>();
+
+		for (int i = 0; i < NUM_FILE_REL_IN; i++) {
+			FileRelease fileRelease = new FileRelease();
+			fileRelease.receiver = TEST_USERNAME_SELF;
+			fileRelease.sourceName = "MDMENV";
+			fileRelease.typeName = "TestStep";
+			fileRelease.id = String.valueOf(i);
+			fileRelease.sender = TEST_USERNAME_OTHER;
+			fileRelease.identifier = ID_IN_PREFIX + i;
+			fileRelease.state = FileReleaseManager.FILE_RELEASE_STATE_ORDERED;
+			fileReleasMockList.add(fileRelease);
+		}
+
+		for (int i = 0; i < NUM_FILE_REL_OUT; i++) {
+			FileRelease fileRelease = new FileRelease();
+			fileRelease.sourceName = "MDMENV";
+			fileRelease.typeName = "TestStep";
+			fileRelease.id = String.valueOf(i + NUM_FILE_REL_IN);
+			fileRelease.receiver = TEST_USERNAME_OTHER;
+			fileRelease.identifier = ID_OUT_PREFIX + i;
+			fileRelease.sender = TEST_USERNAME_SELF;
+			fileRelease.state = FileReleaseManager.FILE_RELEASE_STATE_ORDERED;
+			fileReleasMockList.add(fileRelease);
+		}
+		return fileReleasMockList;
+
+	}
+
+	private static <T extends Entity> T createEntityMock(Class<T> type, String name, String sourceName, String id)
+			throws Exception {
+
+		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);
+		when(core.getValues()).thenReturn(map);
+
+		EntityStore entityStore = new EntityStore();
+		when(core.getMutableStore()).thenReturn(entityStore);
+
+		Constructor<T> constructor = type.getDeclaredConstructor(Core.class);
+		constructor.setAccessible(true);
+		T instance = constructor.newInstance(core);
+		constructor.setAccessible(false);
+		return instance;
+	}
+}
diff --git a/org.eclipse.mdm.filerelease/src/test/java/org/eclipse/mdm/filerelease/FileReleaseServiceTest.java b/org.eclipse.mdm.filerelease/src/test/java/org/eclipse/mdm/filerelease/FileReleaseServiceTest.java
new file mode 100644
index 0000000..e808273
--- /dev/null
+++ b/org.eclipse.mdm.filerelease/src/test/java/org/eclipse/mdm/filerelease/FileReleaseServiceTest.java
@@ -0,0 +1,189 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.filerelease;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+import org.eclipse.mdm.filerelease.boundary.FileReleaseService;
+import org.eclipse.mdm.filerelease.control.FileReleaseException;
+import org.eclipse.mdm.filerelease.control.FileReleaseManager;
+import org.eclipse.mdm.filerelease.entity.FileRelease;
+import org.junit.Test;
+
+public class FileReleaseServiceTest {
+
+	@Test
+	public void testGetReleases() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+		List<FileRelease> fileReleases = fileReleaseService.getReleases(FileReleaseManager.FILE_RELEASE_STATE_ORDERED);
+		assertNotNull("The fileReleases list should not be null.", fileReleases);
+		int expectedSize = FileReleaseServiceMockHelper.NUM_FILE_REL_IN + FileReleaseServiceMockHelper.NUM_FILE_REL_OUT;
+		assertEquals("The size of the fileRelease list should be " + expectedSize, expectedSize, fileReleases.size());
+		fileReleases = fileReleaseService.getReleases("some unknown state");
+		assertEquals("The size of the fileRelease list should be " + 0, 0, fileReleases.size());
+
+	}
+
+	@Test
+	public void testGetRelease() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+		FileRelease fileRelease = fileReleaseService.getRelease(FileReleaseServiceMockHelper.ID_IN_PREFIX + "1");
+		assertNotNull("The file release shoult not be null", fileRelease);
+	}
+
+	@Test
+	public void testGetIncommingReleases() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+		List<FileRelease> fileReleasesIn = fileReleaseService
+				.getIncommingReleases(FileReleaseManager.FILE_RELEASE_STATE_ORDERED);
+		assertNotNull("The fileReleases list should not be null.", fileReleasesIn);
+		assertEquals("The size of the fileRelease list should be " + FileReleaseServiceMockHelper.NUM_FILE_REL_IN,
+				FileReleaseServiceMockHelper.NUM_FILE_REL_IN, fileReleasesIn.size());
+	}
+
+	@Test
+	public void testGetOutgoingReleases() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+		List<FileRelease> fileReleasesOut = fileReleaseService
+				.getOutgoingReleases(FileReleaseManager.FILE_RELEASE_STATE_ORDERED);
+		assertNotNull("The fileReleases list should not be null.", fileReleasesOut);
+		assertEquals("The size of the fileRelease list should be " + FileReleaseServiceMockHelper.NUM_FILE_REL_OUT,
+				FileReleaseServiceMockHelper.NUM_FILE_REL_OUT, fileReleasesOut.size());
+	}
+
+	@Test
+	public void testCreate() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+
+		FileRelease request1 = new FileRelease();
+		request1.sourceName = "MDMENV";
+		request1.typeName = "TestStep";
+		request1.id = "123";
+		request1.format = FileReleaseManager.CONVERTER_FORMAT_PAK2RAW;
+		request1.orderMessage = "new file release";
+		request1.validity = 10;
+		List<FileRelease> fileReleases = fileReleaseService.getReleases(null);
+		fileReleaseService.create(request1);
+
+		FileRelease request2 = new FileRelease();
+		request2.sourceName = "MDMENV";
+		request2.typeName = "TestStep";
+		request2.id = "1234";
+		request2.format = FileReleaseManager.CONVERTER_FORMAT_PAK2ATFX;
+		request2.orderMessage = "new file release";
+		request2.validity = 10;
+		fileReleaseService.create(request2);
+		fileReleases = fileReleaseService.getReleases(null);
+		assertNotNull("The fileReleases list should not be null.", fileReleases);
+		int expectedSize = FileReleaseServiceMockHelper.NUM_FILE_REL_IN + FileReleaseServiceMockHelper.NUM_FILE_REL_OUT
+				+ 2;
+		assertEquals("The size of the fileRelease list should be " + expectedSize, expectedSize, fileReleases.size());
+	}
+
+	@Test(expected = FileReleaseException.class)
+	public void testCreateWithInvalidFileRelease() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+		FileRelease request = new FileRelease();
+		request.sourceName = "MDMENV";
+		request.typeName = "TestStep";
+		request.id = "123";
+		request.format = FileReleaseManager.CONVERTER_FORMAT_PAK2RAW;
+		request.orderMessage = "new file release";
+		request.validity = 0;
+		fileReleaseService.create(request);
+	}
+
+	@Test(expected = FileReleaseException.class)
+	public void testCreateWithUnsuppotedType() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+		FileRelease request = new FileRelease();
+		request.sourceName = "MDMENV";
+		request.typeName = "TestStep";
+		request.id = "123";
+		request.format = "Some unsupported type";
+		request.orderMessage = "new file release";
+		request.validity = 0;
+		fileReleaseService.create(request);
+	}
+
+	@Test
+	public void testDelete() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+		fileReleaseService.delete(FileReleaseServiceMockHelper.ID_OUT_PREFIX + "1");
+		List<FileRelease> fileReleases = fileReleaseService.getReleases(FileReleaseManager.FILE_RELEASE_STATE_ORDERED);
+		assertNotNull("The fileReleases list should not be null.", fileReleases);
+		int expectedSize = FileReleaseServiceMockHelper.NUM_FILE_REL_IN + FileReleaseServiceMockHelper.NUM_FILE_REL_OUT
+				- 1;
+		assertEquals("The size of the fileRelease list should be " + expectedSize, expectedSize, fileReleases.size());
+	}
+
+	@Test
+	public void testApprove() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+		FileRelease release2Approve = new FileRelease();
+		release2Approve.identifier = FileReleaseServiceMockHelper.ID_IN_PREFIX + "1";
+		release2Approve.state = FileReleaseManager.FILE_RELEASE_STATE_APPROVED;
+		fileReleaseService.approve(release2Approve);
+	}
+
+	@Test
+	public void testReject() throws Exception {
+		FileReleaseService fileReleaseService = createMockedService();
+
+		FileRelease release2Reject = new FileRelease();
+		release2Reject.identifier = FileReleaseServiceMockHelper.ID_IN_PREFIX + "1";
+		release2Reject.state = FileReleaseManager.FILE_RELEASE_STATE_REJECTED;
+		release2Reject.rejectMessage = "reject message";
+
+		fileReleaseService.reject(release2Reject);
+
+		FileRelease fileRelease = fileReleaseService.getRelease(FileReleaseServiceMockHelper.ID_IN_PREFIX + "1");
+
+		assertNotNull("FileRelease should not be null.", fileRelease);
+		assertEquals("FileRelease state should be " + FileReleaseManager.FILE_RELEASE_STATE_REJECTED, fileRelease.state,
+				FileReleaseManager.FILE_RELEASE_STATE_REJECTED);
+	}
+
+	private FileReleaseService createMockedService() throws Exception {
+		FileReleaseService fileReleaseService = new FileReleaseService();
+
+		Field fileReleaseManagerField = fileReleaseService.getClass().getDeclaredField("manager");
+		fileReleaseManagerField.setAccessible(true);
+		fileReleaseManagerField.set(fileReleaseService, FileReleaseServiceMockHelper.createFileReleaseManagerMock());
+		fileReleaseManagerField.setAccessible(false);
+
+		Field connectorServiceField = fileReleaseService.getClass().getDeclaredField("connectorService");
+		connectorServiceField.setAccessible(true);
+		connectorServiceField.set(fileReleaseService, FileReleaseServiceMockHelper.createConnectorServiceMock());
+		connectorServiceField.setAccessible(false);
+
+		Field converterField = fileReleaseService.getClass().getDeclaredField("converter");
+		converterField.setAccessible(true);
+		converterField.set(fileReleaseService, FileReleaseServiceMockHelper.createFileConvertJobManagerMock());
+		converterField.setAccessible(false);
+
+		Field targetDirectoryPathField = fileReleaseService.getClass().getDeclaredField("targetDirectoryPath");
+		targetDirectoryPathField.setAccessible(true);
+		targetDirectoryPathField.set(fileReleaseService, System.getProperty("java.io.tmpdir"));
+		targetDirectoryPathField.setAccessible(false);
+
+		return fileReleaseService;
+	}
+}
diff --git a/org.eclipse.mdm.freetextindexer/.gitignore b/org.eclipse.mdm.freetextindexer/.gitignore
new file mode 100644
index 0000000..b67ba80
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/.gitignore
@@ -0,0 +1,9 @@
+/build/
+.classpath
+.project
+/bin/
+/.gradle/
+
+/..gitignore.un~
+/.gitignore~
+/.gradle/
diff --git a/org.eclipse.mdm.freetextindexer/WebContent/META-INF/MANIFEST.MF b/org.eclipse.mdm.freetextindexer/WebContent/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5e94951
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/WebContent/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0

+Class-Path: 

+

diff --git a/org.eclipse.mdm.freetextindexer/build.gradle b/org.eclipse.mdm.freetextindexer/build.gradle
new file mode 100644
index 0000000..13b961f
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/build.gradle
@@ -0,0 +1,51 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+ 
+
+description = 'MDM FreeText Search Indexer'
+
+apply plugin: 'war'
+apply plugin: 'maven'
+apply plugin: 'eclipse'
+
+repositories {
+	jcenter()
+	mavenLocal()
+}
+
+dependencies {
+	compile 'commons-httpclient:commons-httpclient:3.1'
+	compile 'org.slf4j:jcl-over-slf4j:1.7.25'
+
+	compileOnly 'com.fasterxml.jackson.core:jackson-databind:2.5.1'
+
+	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 project(':org.eclipse.mdm.connector')
+	compile project(':org.eclipse.mdm.property')
+
+	compileOnly 'javax:javaee-api:7.0'
+
+	testCompile 'junit:junit:4.12'
+	testCompile 'org.apache.lucene:lucene-test-framework:5.3.1'
+	testCompile(group: 'org.elasticsearch', name: 'elasticsearch', version: '2.3.4')
+	testCompile(group: 'org.elasticsearch', name: 'elasticsearch', version: '2.3.4', classifier: 'tests')
+  testRuntimeClasspath 'com.fasterxml.jackson.core:jackson-databind:2.6.6'
+}
+
+jar {
+	metaInf { from '../NOTICE.txt' }
+	metaInf { from '../LICENSE.txt' }
+}
diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/ElasticsearchBoundary.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/ElasticsearchBoundary.java
new file mode 100644
index 0000000..930242a
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/ElasticsearchBoundary.java
@@ -0,0 +1,184 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.boundary;

+

+import java.io.IOException;

+import java.text.SimpleDateFormat;

+

+import javax.ejb.Stateless;

+import javax.ejb.TransactionAttribute;

+import javax.ejb.TransactionAttributeType;

+import javax.inject.Inject;

+

+import org.apache.commons.httpclient.HttpClient;

+import org.apache.commons.httpclient.HttpMethod;

+import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;

+import org.apache.commons.httpclient.methods.DeleteMethod;

+import org.apache.commons.httpclient.methods.GetMethod;

+import org.apache.commons.httpclient.methods.PutMethod;

+import org.eclipse.mdm.freetextindexer.entities.MDMEntityResponse;

+import org.eclipse.mdm.property.GlobalProperty;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+import com.fasterxml.jackson.databind.ObjectMapper;

+

+/**

+ * This Boundary is back-end only to the ElasticSearch Server. It is responsible

+ * for the actual indexing work.

+ * 

+ * @author CWE

+ *

+ */

+@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)

+@Stateless

+public class ElasticsearchBoundary {

+

+	private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchBoundary.class);

+

+	private ObjectMapper jsonMapper;

+	private HttpClient client;

+

+	@Inject

+	@GlobalProperty(value = "elasticsearch.url")

+	String esAddress;

+

+	@Inject

+	@GlobalProperty(value = "freetext.active")

+	String active;

+

+	/**

+	 * Connects to the ElasticSearch Server

+	 * 

+	 * @throws IOException

+	 */

+	public ElasticsearchBoundary() {

+		jsonMapper = new ObjectMapper();

+		jsonMapper.setDateFormat(new SimpleDateFormat("yyyyMMdd'T'HHmmssZ"));

+		client = new HttpClient();

+	}

+

+	public void index(MDMEntityResponse document) {

+		try {

+			PutMethod put = new PutMethod(esAddress + getPath(document) + "?ignore_conflicts=true");

+

+			byte[] json = jsonMapper.writeValueAsBytes(document);

+			LOGGER.trace("Document {}: {}", getPath(document), new String(json));

+

+			put.setRequestEntity(new ByteArrayRequestEntity(json, "application/json"));

+

+			execute(put);

+

+		} catch (IOException e) {

+			throw new IllegalStateException(e);

+		}

+	}

+

+	private String getPath(MDMEntityResponse document) {

+		return document.source.toLowerCase() + "/" + document.type + "/" + document.id;

+	}

+

+	private void execute(HttpMethod put) {

+		try {

+			int status = client.executeMethod(put);

+			

+			checkError(status, put);

+		} catch (IOException e) {

+			throw new IllegalStateException("Problems querying ElasticSearch.", e);

+		}

+	}

+

+	private void checkError(int status, HttpMethod method) {

+		String text = String.format("ElasticSearch answered %d. ", status);

+

+		int httpCategory = status / 100;

+		switch (httpCategory) {

+		case 4:

+			text = text + "This indicates a Client error: ";

+			break;

+		case 5:

+			text = text + "This indicates a Server error. The ES instance must be checked (" + esAddress + "): ";

+			break;

+		}

+

+		try {

+			if (httpCategory != 2) {

+				throw new IllegalStateException(text + method.getResponseBodyAsString());

+			}

+		} catch (IOException e) {

+			throw new IllegalStateException(text + "\nError occured during reading the elastic search response!", e);

+		}

+	}

+

+	public void delete(String api, String type, String id) {

+		String path = api.toLowerCase() + "/" + type + "/" + id;

+		DeleteMethod put = new DeleteMethod(esAddress + path);

+

+		execute(put);

+

+		if (LOGGER.isDebugEnabled()) {

+			LOGGER.debug("Document '{}' has been deleted!", path);

+		}

+	}

+

+	public boolean hasIndex(String source) {

+		boolean hasIndex = false;

+

+		if (active()) {

+			try {

+				GetMethod get = new GetMethod(esAddress + source.toLowerCase());

+				int status = client.executeMethod(get);

+				LOGGER.info("Checking index {}: {}", source, status);

+

+				hasIndex = status / 100 == 2;

+			} catch (IOException e) {

+				LOGGER.warn("Querying ElasticSearch for the Index failed... Assuming no index is there!", e);

+				hasIndex = false;

+			}

+		}

+

+		return hasIndex;

+	}

+

+	public void createIndex(String source) {

+		if (Boolean.valueOf(active)) {

+			execute(new PutMethod(esAddress + source.toLowerCase()));

+			LOGGER.info("New Index created!");

+		}

+	}

+

+	public void validateConnectionIsPossible() {

+		if(active()) {

+			try {

+				GetMethod get = new GetMethod(esAddress);

+				int status = client.executeMethod(get);

+				if(status /100 != 2) {

+					LOGGER.error("Cannot connect to elasticsearch at {} but free text search is enabled! http status was: {}", esAddress, status);

+					throw new RuntimeException("Cannot connect to elasticsearch.");

+				} else {

+					LOGGER.info("Successfully connected to elasticsearch!");

+				}

+

+			} catch (IOException e) {

+				LOGGER.error("Cannot connect to elasticsearch at {} but free text search is enabled!", esAddress, e);

+				throw new RuntimeException("Cannot connect to elasticsearch.", e);

+			}

+		}

+	}

+

+	public boolean active() {

+		return Boolean.valueOf(active);

+	}

+}

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
new file mode 100644
index 0000000..d042944
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/MdmApiBoundary.java
@@ -0,0 +1,233 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.freetextindexer.boundary;
+
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.ejb.Startup;
+import javax.ejb.Stateful;
+import javax.ejb.TransactionAttribute;
+import javax.ejb.TransactionAttributeType;
+import javax.inject.Inject;
+
+import org.eclipse.mdm.api.base.ConnectionException;
+import org.eclipse.mdm.api.base.ServiceNotProvidedException;
+import org.eclipse.mdm.api.base.adapter.EntityType;
+import org.eclipse.mdm.api.base.model.Entity;
+import org.eclipse.mdm.api.base.model.User;
+import org.eclipse.mdm.api.base.notification.NotificationException;
+import org.eclipse.mdm.api.base.notification.NotificationFilter;
+import org.eclipse.mdm.api.base.notification.NotificationListener;
+import org.eclipse.mdm.api.base.notification.NotificationService;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.connector.boundary.ConnectorService;
+import org.eclipse.mdm.freetextindexer.control.UpdateIndex;
+import org.eclipse.mdm.freetextindexer.entities.MDMEntityResponse;
+import org.eclipse.mdm.property.GlobalProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This boundary is a back-end Boundary to the openMDM Api. It uses the Seach
+ * Server to build up MDDocuments.
+ * 
+ * @author CWE
+ *
+ */
+@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)
+@Startup
+@Stateful
+public class MdmApiBoundary {
+
+	private static final Logger LOGGER = LoggerFactory.getLogger(MdmApiBoundary.class);
+
+	private static final String FREETEXT_NOTIFICATION_NAME = "freetext.notificationName";
+
+	public class FreeTextNotificationListener implements NotificationListener {
+
+		private EntityManager entityManager;
+
+		public FreeTextNotificationListener(EntityManager entityManager) {
+			this.entityManager = entityManager;
+		}
+
+		@Override
+		public void instanceCreated(List<? extends Entity> entities, User arg1) {
+			LOGGER.debug("{} entities created: {}", entities.size(), entities);
+			entities.forEach(e -> update.change(MDMEntityResponse.build(e.getClass(), e, entityManager)));
+		}
+
+		@Override
+		public void instanceDeleted(EntityType entityType, List<String> ids, User user) {
+			LOGGER.debug("{} entities deleted: {}", ids.size(), ids);
+			ids.forEach(id -> update.delete(getApiName(entityManager), workaroundForTypeMapping(entityType), id));
+		}
+
+		@Override
+		public void instanceModified(List<? extends Entity> entities, User arg1) {
+			LOGGER.debug("{} entities modified: {}", entities.size(), entities);
+			entities.forEach(e -> update.change(MDMEntityResponse.build(e.getClass(), e, entityManager)));
+		}
+
+		@Override
+		public void modelModified(EntityType arg0, User arg1) {
+			// not needed
+		}
+
+		@Override
+		public void securityModified(EntityType entityType, List<String> ids, User user) {
+			// not needed
+		}
+	}
+
+	@Inject
+	ConnectorService service;
+
+	@Inject
+	UpdateIndex update;
+
+	@Inject
+	@GlobalProperty(value = "freetext.active")
+	private String active = "false";
+
+	ConnectorService connectorService; 
+
+	@Inject
+	@GlobalProperty
+	private Map<String, String> globalProperties = Collections.emptyMap();
+
+	@PostConstruct
+	public void initalize() {
+		Principal principal = new Principal() {
+			
+			@Override
+			public String getName() {
+				return null;
+			}
+		};
+		
+		connectorService = new ConnectorService(principal, globalProperties);
+		connectorService.connect();
+		connectorService.getContexts().forEach(this::initializeContext);
+	}
+
+	private void initializeContext(ApplicationContext context) {
+
+		String source = getName(context);
+
+		try {
+			EntityManager entityManager = context.getEntityManager()
+					.orElseThrow(() -> new ServiceNotProvidedException(EntityManager.class));
+
+			NotificationService manager = context.getNotificationService()
+					.orElseThrow(() -> new ConnectionException("Context has no NotificationManager!"));
+
+			String notificationName = context.getParameters().getOrDefault(FREETEXT_NOTIFICATION_NAME, "mdm5");
+			LOGGER.debug("Registering with name '{}' at source '{}'", notificationName, source);
+			
+			manager.register(notificationName, new NotificationFilter(), new FreeTextNotificationListener(entityManager));
+			LOGGER.info("Successfully registered for new notifications with name '{}' at source '{}!", notificationName, source);
+		} catch (ConnectionException | NotificationException e) {
+			throw new IllegalArgumentException("The ODS Server and/or the Notification Service cannot be accessed for source '" + source + "'!",
+					e);
+		}
+	}
+
+	@PreDestroy
+	public void deregister() {
+		for (ApplicationContext context : getContexts().values()) {
+			try {
+				context.getNotificationService()
+					.orElseThrow(() -> new ConnectionException("Context has no NotificationManager!"))
+					.close(false);
+
+			} catch (ConnectionException | NotificationException e) {
+				throw new IllegalStateException(
+						"The NotificationManager could not be deregistered. In rare cases, this leads to a missed notification. This means the index might not be up-to-date.");
+			}
+		}
+		
+		connectorService.disconnect();
+	}
+
+	public void doForAllEntities(Class<? extends Entity> entityClass, ApplicationContext context, Consumer<? super MDMEntityResponse> executor) {
+		if (isActive()) {
+			try {
+				EntityManager entityManager = context.getEntityManager()
+						.orElseThrow(() -> new ServiceNotProvidedException(EntityManager.class));
+
+				entityManager.loadAll(entityClass).stream().map(e -> this.buildEntity(e, entityManager)).forEach(executor);
+
+			} catch (DataAccessException e) {
+				throw new IllegalStateException("MDM cannot be querried for new elements. Please check the MDM runtime", e);
+			}
+		}
+	}
+
+	public Map<String, ApplicationContext> getContexts() {
+		return connectorService.getContexts().stream().collect(Collectors.toMap(this::getName, Function.identity()));
+	}
+
+	public String getName(ApplicationContext context) {
+		return context.getEntityManager()
+				.map(this::getApiName)
+				.orElseThrow(() -> new ServiceNotProvidedException(EntityManager.class));
+	}
+
+	private String getApiName(EntityManager entityManager) {
+		return entityManager.loadEnvironment().getSourceName();
+	}
+
+	private boolean isActive() {
+		return Boolean.parseBoolean(active) && !getContexts().isEmpty();
+	}
+
+	private MDMEntityResponse buildEntity(Entity e, EntityManager entityManager) {
+		return MDMEntityResponse.build(e.getClass(), e, entityManager);
+	}
+
+	/**
+	 * Simple workaround for naming mismatch between Adapter and Business object
+	 * names.
+	 *
+	 * @param entityType
+	 *            entity type
+	 * @return MDM business object name
+	 */
+	private 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();
+		}
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/package-info.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/package-info.java
new file mode 100644
index 0000000..e987d06
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/boundary/package-info.java
@@ -0,0 +1,31 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+/**

+ * This package contains all communication to external systems. Those systems

+ * are:

+ * <ul>

+ * <li>ElasticSearch for Indexing via REST

+ * {@link org.eclipse.mdm.freetextindexer.boundary.ElasticsearchBoundary

+ * ElasticsearchBoundary}</li>

+ * <li>MDM for querying the dataitems and getting notifications

+ * {@link org.eclipse.mdm.freetextindexer.boundary.MdmApiBoundary

+ * MDMBoundary}</li>

+ * <li>JEE Container for creating the initial index

+ * {@link org.eclipse.mdm.freetextindexer.boundary.StartupBoundary

+ * StartupBoundary}</li>

+ * </ul>

+ * 

+ */

+package org.eclipse.mdm.freetextindexer.boundary;

diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/control/SetupIndex.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/control/SetupIndex.java
new file mode 100644
index 0000000..b977252
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/control/SetupIndex.java
@@ -0,0 +1,78 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.control;

+

+import java.util.Map;

+

+import javax.annotation.PostConstruct;

+import javax.ejb.Asynchronous;

+import javax.ejb.EJB;

+import javax.ejb.Singleton;

+import javax.ejb.Startup;

+import javax.ejb.TransactionAttribute;

+import javax.ejb.TransactionAttributeType;

+import javax.enterprise.event.Event;

+import javax.enterprise.event.Observes;

+import javax.inject.Inject;

+

+import org.eclipse.mdm.api.base.model.Entity;

+import org.eclipse.mdm.api.base.model.Measurement;

+import org.eclipse.mdm.api.base.model.Test;

+import org.eclipse.mdm.api.base.model.TestStep;

+import org.eclipse.mdm.api.dflt.ApplicationContext;

+import org.eclipse.mdm.freetextindexer.boundary.ElasticsearchBoundary;

+import org.eclipse.mdm.freetextindexer.boundary.MdmApiBoundary;

+import org.eclipse.mdm.freetextindexer.events.CreateIndex;

+import org.slf4j.Logger;

+import org.slf4j.LoggerFactory;

+

+@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)

+@Startup

+@Singleton

+public class SetupIndex {

+

+	private static final Logger LOGGER = LoggerFactory.getLogger(SetupIndex.class);

+

+	@EJB

+	ElasticsearchBoundary esBoundary;

+

+	@EJB

+	MdmApiBoundary apiBoundary;

+

+	@Inject

+	Event<CreateIndex> createIndexEvent;

+

+	@PostConstruct

+	public void createIndexIfNeccessary() {

+		for (Map.Entry<String, ApplicationContext> entry : apiBoundary.getContexts().entrySet()) {

+			String source = entry.getKey();

+

+			if (!esBoundary.hasIndex(source)) {

+				LOGGER.info("About to create new lucene index!");

+				createIndexEvent.fire(new CreateIndex(source, Test.class, TestStep.class, Measurement.class));

+			}

+		}

+	}

+

+	@Asynchronous

+	public void handleCreateIndexEvent(@Observes CreateIndex event) {

+		String source = event.getSourceName();

+		esBoundary.createIndex(source);

+		for(Class<? extends Entity> entityType : event.getEntitiesToIndex()) {

+			apiBoundary.doForAllEntities(entityType, apiBoundary.getContexts().get(source), e -> esBoundary.index(e));

+		}

+		LOGGER.info("Index '" + source + "' initialized");

+	}

+}

diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/control/UpdateIndex.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/control/UpdateIndex.java
new file mode 100644
index 0000000..0a8e3ba
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/control/UpdateIndex.java
@@ -0,0 +1,43 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.control;

+

+import javax.ejb.EJB;

+import javax.ejb.Singleton;

+import javax.ejb.Startup;

+import javax.ejb.TransactionAttribute;

+import javax.ejb.TransactionAttributeType;

+

+import org.eclipse.mdm.freetextindexer.boundary.ElasticsearchBoundary;

+import org.eclipse.mdm.freetextindexer.entities.MDMEntityResponse;

+

+@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)

+@Startup

+@Singleton

+public class UpdateIndex {

+

+	@EJB

+	ElasticsearchBoundary esBoundary;

+

+	public void change(MDMEntityResponse mdmEntityResponse) {

+		if (mdmEntityResponse != null) {

+			esBoundary.index(mdmEntityResponse);

+		}

+	}

+

+	public void delete(String apiName, String name, String id) {

+		esBoundary.delete(apiName, name, id);

+	}

+}

diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/control/package-info.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/control/package-info.java
new file mode 100644
index 0000000..85d3640
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/control/package-info.java
@@ -0,0 +1,15 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.control;

diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/MDMEntity.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/MDMEntity.java
new file mode 100644
index 0000000..72f5252
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/MDMEntity.java
@@ -0,0 +1,95 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.freetextindexer.entities;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.mdm.api.base.model.BaseEntity;
+import org.eclipse.mdm.api.base.model.Value;
+
+/**
+ * MDMEntity (Entity for a business object (contains a list of
+ * {@link MDMAttribute}s)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class MDMEntity {
+
+	private static final Set<String> NOTINDEXED = new HashSet<>(
+			Arrays.asList(BaseEntity.ATTR_ID, BaseEntity.ATTR_MIMETYPE, "Optional", "SortIndex"));
+
+	/** name of the MDM business object */
+	public final String name;
+	
+	/** type of the MDM business object */
+	public final String type;
+
+	/** id of the MDM business object */
+	public final String id;
+
+	/** list of attribute to transfer */
+	public final Map<String, String> attributes;
+
+	public final List<MDMEntity> components = new ArrayList<>();
+
+	/**
+	 * Constructor
+	 * 
+	 * @param name
+	 *            name of the MDM business object
+	 * @param type
+	 *            type as String of the MDM business object (e.g. TestStep)
+	 * @param uri
+	 *            URI of an MDM business object
+	 * @param values
+	 *            values of a MDM business object
+	 */
+	public MDMEntity(String name, String type, String id, Map<String, Value> values) {
+		this.name = name;
+		this.type = type;
+		this.id = id;
+
+		this.attributes = initAttributes(values);
+	}
+
+	/**
+	 * converts the MDM business object values to string values
+	 * 
+	 * @param values
+	 *            values of a MDM business object
+	 * @return list with converted attribute values
+	 */
+	private Map<String, String> initAttributes(Map<String, Value> values) {
+		Map<String, String> mapAttrs = new HashMap<>();
+		for (java.util.Map.Entry<String, Value> entry : values.entrySet()) {
+
+			String key = entry.getKey();
+			if (!NOTINDEXED.contains(key) && entry.getValue().isValid()) {
+				mapAttrs.put(key, entry.getValue().extract().toString());
+			}
+		}
+		
+		return mapAttrs;
+	}
+
+}
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
new file mode 100644
index 0000000..a1a536d
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityResponse.java
@@ -0,0 +1,99 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.freetextindexer.entities;
+
+import org.eclipse.mdm.api.base.model.*;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.dflt.EntityManager;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+/**
+ * EntryResponse (Container for {@link MDMEntity}s)
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class MDMEntityResponse {
+
+	public String source;
+
+	public String type;
+
+	/** transferable data content */
+	public MDMEntity data;
+
+	public String id;
+
+	/**
+	 * Constructor (for a list of business objects {@link MDMEntity}s)
+	 * 
+	 * @param type
+	 *            type of all containging {@link MDMEntity}s
+	 * @param entries
+	 *            list of {@link MDMEntity}
+	 * @throws DataAccessException
+	 */
+	public static <T extends Entity> MDMEntityResponse build(Class<? extends Entity> type, T businessObject,
+			EntityManager manager) {
+		MDMEntityResponse response = new MDMEntityResponse();
+
+		try {
+			response.type = type.getSimpleName();
+			response.source = businessObject.getSourceName();
+			response.data = toTransferable(businessObject);
+			response.id = businessObject.getID();
+
+			response.addContext(businessObject, manager);
+		} catch (DataAccessException e) {
+			response = null;
+		}
+
+		return response;
+	}
+
+	private <T extends Entity> void addContext(T businessObject, EntityManager manager) throws DataAccessException {
+		if (businessObject instanceof ContextDescribable) {
+			Map<ContextType, ContextRoot> contexts = manager.loadContexts((ContextDescribable) businessObject,
+					ContextType.UNITUNDERTEST, ContextType.TESTSEQUENCE, ContextType.TESTEQUIPMENT);
+
+			for (ContextRoot root : contexts.values()) {
+				MDMEntity entity = toTransferable(root);
+				data.components.add(entity);
+				for (ContextComponent comp : root.getContextComponents()) {
+					MDMEntity compEntity = toTransferable(comp);
+					entity.components.add(compEntity);
+					for (Entry<String, Value> entry : comp.getValues().entrySet()) {
+						Optional<Object> extractedValue = Optional.ofNullable(entry.getValue().extract());
+						compEntity.attributes.put(entry.getKey(), extractedValue.map(Object::toString).orElse(""));
+					}
+				}
+			}
+		}
+	}
+
+	private static <T extends Entity> MDMEntity toTransferable(T businessObject) {
+		return new MDMEntity(businessObject.getName(), businessObject.getClass().getSimpleName(),
+				businessObject.getID(), businessObject.getValues());
+	}
+}
diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/package-info.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/package-info.java
new file mode 100644
index 0000000..d69f629
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/entities/package-info.java
@@ -0,0 +1,15 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.entities;

diff --git a/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/events/CreateIndex.java b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/events/CreateIndex.java
new file mode 100644
index 0000000..ac9384c
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/main/java/org/eclipse/mdm/freetextindexer/events/CreateIndex.java
@@ -0,0 +1,55 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.freetextindexer.events;
+
+import com.google.common.collect.ImmutableList;
+import org.eclipse.mdm.api.base.model.Entity;
+
+import java.util.Objects;
+
+public class CreateIndex {
+    private final String sourceName;
+    private final ImmutableList<Class<? extends Entity>> entitiesToIndex;
+
+    @SafeVarargs
+    public CreateIndex(String sourceName, Class<? extends Entity> ... entitiesToIndex) {
+        this.sourceName = Objects.requireNonNull(sourceName, "Source name may never be null!");
+        this.entitiesToIndex = ImmutableList.copyOf(entitiesToIndex);
+    }
+
+    public ImmutableList<Class<? extends Entity>> getEntitiesToIndex() {
+        return entitiesToIndex;
+    }
+
+
+    public String getSourceName() {
+        return sourceName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        CreateIndex that = (CreateIndex) o;
+        return Objects.equals(sourceName, that.sourceName) &&
+                Objects.equals(entitiesToIndex, that.entitiesToIndex);
+    }
+
+    @Override
+    public int hashCode() {
+
+        return Objects.hash(sourceName, entitiesToIndex);
+    }
+}
diff --git a/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/boundary/ElasticsearchBoundaryTest.java b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/boundary/ElasticsearchBoundaryTest.java
new file mode 100644
index 0000000..523f95d
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/boundary/ElasticsearchBoundaryTest.java
@@ -0,0 +1,177 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.boundary;

+

+import static org.junit.Assert.assertEquals;

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertTrue;

+import static org.mockito.ArgumentMatchers.any;

+import static org.mockito.Mockito.mock;

+import static org.mockito.Mockito.when;

+

+import java.io.File;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.UUID;

+import java.util.concurrent.ExecutionException;

+

+import org.eclipse.mdm.api.base.model.ContextComponent;

+import org.eclipse.mdm.api.base.model.ContextRoot;

+import org.eclipse.mdm.api.base.model.ContextType;

+import org.eclipse.mdm.api.base.model.TestStep;

+import org.eclipse.mdm.api.base.model.Value;

+import org.eclipse.mdm.api.base.query.DataAccessException;

+import org.eclipse.mdm.api.dflt.EntityManager;

+import org.eclipse.mdm.freetextindexer.entities.MDMEntityResponse;

+import org.elasticsearch.action.search.SearchRequestBuilder;

+import org.elasticsearch.action.search.SearchResponse;

+import org.elasticsearch.client.Client;

+import org.elasticsearch.common.settings.Settings;

+import org.elasticsearch.index.query.QueryBuilders;

+import org.elasticsearch.node.Node;

+import org.elasticsearch.node.NodeBuilder;

+import org.elasticsearch.search.SearchHits;

+import org.junit.AfterClass;

+import org.junit.Before;

+import org.junit.BeforeClass;

+import org.junit.Ignore;

+import org.junit.Test;

+

+public class ElasticsearchBoundaryTest {

+

+	private static Node elasticSearchNode;

+	private static ElasticsearchBoundary es;

+

+	private static Client client;

+

+	@BeforeClass

+	public static void beforeClass() throws Exception {

+		File tempDir = File.createTempFile("elasticsearch-temp", Long.toString(System.nanoTime()));

+		tempDir.delete();

+		tempDir.mkdir();

+

+		String clusterName = UUID.randomUUID().toString();

+		elasticSearchNode = NodeBuilder.nodeBuilder().local(true).clusterName(clusterName)

+				.settings(Settings.settingsBuilder()

+						.put("path.home", File.createTempFile("elasticsearch", "").getParent())

+						.put("index.number_of_shards", 1).put("index.number_of_replicas", 0).put("http.port", 9301))

+				.node();

+		elasticSearchNode.start();

+		client = elasticSearchNode.client();

+

+		es = new ElasticsearchBoundary();

+		es.esAddress = "http://localhost:9301/";

+	}

+

+	@AfterClass

+	public static void afterClass() {

+		elasticSearchNode.close();

+	}

+

+	@Before

+	public void setup() {

+		es.active = "true";

+	}

+

+	@Test

+	public void indexSuccessfullyCreated_CaseDoesNotMatter() throws InterruptedException, ExecutionException {

+		es.createIndex("BlA");

+		assertTrue(es.hasIndex("bla"));

+	}

+

+	@Test

+	public void indexSuccessfullyCreated_OtherIndizesNot() throws InterruptedException, ExecutionException {

+		es.createIndex("someIndex");

+		assertFalse(es.hasIndex("asdf"));

+	}

+

+	@Ignore

+	@Test

+	public void deletedDoc_isGone() throws InterruptedException, DataAccessException {

+		TestStep ts = mock(TestStep.class);

+		when(ts.getID()).thenReturn("1");

+		when(ts.getSourceName()).thenReturn("mdmdiff");

+		EntityManager manager = mockManager();

+

+		MDMEntityResponse document = MDMEntityResponse.build(TestStep.class, ts, manager);

+		es.index(document);

+

+		es.delete("mdmdiff", "TestStep", "1");

+

+		assertEquals(0, searchForASDF("mdmdiff").totalHits());

+	}

+

+	@Ignore

+	@Test

+	public void docIsIndexed_isFound() throws DataAccessException, InterruptedException {

+		TestStep ts = mock(TestStep.class);

+		when(ts.getSourceName()).thenReturn("mdm");

+		EntityManager manager = mockManager();

+

+		MDMEntityResponse document = MDMEntityResponse.build(TestStep.class, ts, manager);

+		es.index(document);

+

+		assertEquals(1, searchForASDF("mdm").totalHits());

+	}

+

+	@Test(expected = IllegalStateException.class)

+	public void indexCreatedTwice_ThrowsError() {

+		es.createIndex("someRandomIndex");

+		es.createIndex("someRandomIndex");

+	}

+

+	@Test

+	public void indexDeactivated_NoIndexingDone() {

+		es.active = "false";

+

+		es.createIndex("someSource");

+		assertFalse(es.hasIndex("someSource"));

+	}

+

+	private SearchHits searchForASDF(String index) throws InterruptedException {

+		Thread.sleep(1000); // wait until cluster is healthy again

+

+		SearchRequestBuilder request = client.prepareSearch(index)

+				.setQuery(QueryBuilders.simpleQueryStringQuery("asdf").field("_all").field("name^2").lenient(true));

+		SearchResponse getResponse = request.execute().actionGet();

+		SearchHits hits = getResponse.getHits();

+		return hits;

+	}

+

+	private EntityManager mockManager() throws DataAccessException {

+		EntityManager manager = mock(EntityManager.class);

+		ContextRoot root = mock(ContextRoot.class);

+		ContextComponent comp = mock(ContextComponent.class);

+		Value value = mock(Value.class);

+

+		Map<ContextType, ContextRoot> map = new HashMap<>();

+		List<ContextComponent> comps = new ArrayList<>();

+		Map<String, Value> values = new HashMap<>();

+		values.put("name", value);

+		comps.add(comp);

+

+		when(value.toString()).thenReturn("asdf");

+		when(comp.getValues()).thenReturn(values);

+		when(root.getContextComponents()).thenReturn(comps);

+		map.put(ContextType.UNITUNDERTEST, root);

+

+		when(manager.loadContexts(any(TestStep.class), any())).thenReturn(map);

+

+		return manager;

+	}

+

+}

diff --git a/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/control/SetupIndexTest.java b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/control/SetupIndexTest.java
new file mode 100644
index 0000000..fccead3
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/control/SetupIndexTest.java
@@ -0,0 +1,65 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.control;

+

+import static org.mockito.ArgumentMatchers.any;

+import static org.mockito.Mockito.mock;

+import static org.mockito.Mockito.times;

+import static org.mockito.Mockito.verify;

+import static org.mockito.Mockito.when;

+

+import org.eclipse.mdm.api.dflt.ApplicationContext;

+import org.eclipse.mdm.freetextindexer.boundary.ElasticsearchBoundary;

+import org.eclipse.mdm.freetextindexer.boundary.MdmApiBoundary;

+import org.eclipse.mdm.freetextindexer.events.CreateIndex;

+import org.junit.Before;

+import org.junit.Test;

+

+import com.google.common.collect.ImmutableMap;

+import javax.enterprise.event.Event;

+

+public class SetupIndexTest {

+

+    private SetupIndex setup;

+

+    @Before

+    @SuppressWarnings("unchecked")

+    public void init() {

+        setup = new SetupIndex();

+        setup.esBoundary = mock(ElasticsearchBoundary.class);

+        setup.apiBoundary = mock(MdmApiBoundary.class);

+        setup.createIndexEvent = mock(Event.class);

+    }

+

+    @Test

+    public void hasAlreadyIndex_doNothing() {

+        when(setup.esBoundary.hasIndex(any(String.class))).thenReturn(true);

+

+        setup.createIndexIfNeccessary();

+

+        verify(setup.esBoundary, times(0)).createIndex(any(String.class));

+    }

+

+	@Test

+	public void noIndex_created() {

+		ApplicationContext c = mock(ApplicationContext.class);

+		when(setup.esBoundary.hasIndex(any(String.class))).thenReturn(false);

+		when(setup.apiBoundary.getContexts()).thenReturn(ImmutableMap.of("MDM", c));

+		

+		setup.createIndexIfNeccessary();

+

+        verify(setup.createIndexEvent, times(1)).fire(any(CreateIndex.class));

+    }

+}

diff --git a/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/control/UpdateIndexTest.java b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/control/UpdateIndexTest.java
new file mode 100644
index 0000000..64e0a3d
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/control/UpdateIndexTest.java
@@ -0,0 +1,60 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.control;

+

+import static org.mockito.Matchers.any;

+import static org.mockito.Matchers.eq;

+import static org.mockito.Mockito.mock;

+import static org.mockito.Mockito.never;

+import static org.mockito.Mockito.times;

+import static org.mockito.Mockito.verify;

+

+import org.eclipse.mdm.freetextindexer.boundary.ElasticsearchBoundary;

+import org.eclipse.mdm.freetextindexer.entities.MDMEntityResponse;

+import org.junit.Before;

+import org.junit.Test;

+

+public class UpdateIndexTest {

+

+	private UpdateIndex update;

+

+	@Before

+	public void init() {

+		update = new UpdateIndex();

+		update.esBoundary = mock(ElasticsearchBoundary.class);

+	}

+

+	@Test

+	public void nullGiven_notUpdated() {

+		update.change(null);

+

+		verify(update.esBoundary, never()).index(any(MDMEntityResponse.class));

+	}

+

+	@Test

+	public void validDoc_Indexed() {

+		MDMEntityResponse response = mock(MDMEntityResponse.class);

+		update.change(response);

+

+		verify(update.esBoundary, times(1)).index(eq(response));

+	}

+

+	@Test

+	public void validDoc_deleted() {

+		update.delete("api", "TestStep", "123");

+

+		verify(update.esBoundary, times(1)).delete(eq("api"), eq("TestStep"), eq("123"));

+	}

+}

diff --git a/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityResponseTest.java b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityResponseTest.java
new file mode 100644
index 0000000..233ad6b
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityResponseTest.java
@@ -0,0 +1,50 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.entities;

+

+import static org.junit.Assert.assertNull;

+import static org.junit.Assert.assertTrue;

+import static org.mockito.Mockito.*;

+

+import org.eclipse.mdm.api.base.model.Quantity;

+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.junit.Ignore;

+import org.junit.Test;

+

+public class MDMEntityResponseTest {

+

+	@Ignore

+	@Test

+	public void notContextDescribale_noContext() {

+		Quantity q = mock(Quantity.class);

+		EntityManager manager = mock(EntityManager.class);

+

+		MDMEntityResponse response = MDMEntityResponse.build(Quantity.class, q, manager);

+

+		assertTrue(response.data.attributes.isEmpty());

+	}

+

+	@Ignore

+	@Test

+	public void buildFails_nullIsReturned() throws DataAccessException {

+		TestStep ts = mock(TestStep.class);

+		EntityManager manager = mock(EntityManager.class);

+		when(manager.loadContexts(eq(ts), any())).thenThrow(new DataAccessException("test"));

+

+		assertNull(MDMEntityResponse.build(TestStep.class, ts, manager));

+	}

+}

diff --git a/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityTest.java b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityTest.java
new file mode 100644
index 0000000..53a3e4c
--- /dev/null
+++ b/org.eclipse.mdm.freetextindexer/src/test/java/org/eclipse/mdm/freetextindexer/entities/MDMEntityTest.java
@@ -0,0 +1,59 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+package org.eclipse.mdm.freetextindexer.entities;

+

+import static org.junit.Assert.assertFalse;

+import static org.junit.Assert.assertTrue;

+

+import java.util.HashMap;

+import java.util.Map;

+

+import org.eclipse.mdm.api.base.model.Value;

+import org.eclipse.mdm.api.base.model.ValueType;

+import org.junit.Test;

+

+public class MDMEntityTest {

+

+	@Test

+	public void notIndexedFields_NotIndexed() {

+		MDMEntity entity = buildEntity("SortIndex", true);

+

+		assertTrue(entity.attributes.isEmpty());

+	}

+

+	@Test

+	public void invalidFields_NotIndexed() {

+		MDMEntity entity = buildEntity("indexed", false);

+

+		assertTrue(entity.attributes.isEmpty());

+	}

+

+	@Test

+	public void validField_Indexed() {

+		MDMEntity entity = buildEntity("indexed", true);

+

+		assertFalse(entity.attributes.isEmpty());

+	}

+

+	private MDMEntity buildEntity(String name, boolean valid) {

+		Value value = ValueType.INTEGER.create(name, 123);

+		value.setValid(valid);

+		Map<String, Value> map = new HashMap<>();

+		map.put(value.getName(), value);

+

+		MDMEntity entity = new MDMEntity("name", "", "1", map);

+		return entity;

+	}

+}

diff --git a/org.eclipse.mdm.preferences/build.gradle b/org.eclipse.mdm.preferences/build.gradle
new file mode 100644
index 0000000..69c57a5
--- /dev/null
+++ b/org.eclipse.mdm.preferences/build.gradle
@@ -0,0 +1,73 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+description = 'MDM preference service'
+
+apply plugin: 'java'
+apply plugin: 'maven'
+apply plugin: 'eclipse'
+apply plugin: 'io.github.divinespear.jpa-schema-generate'
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+repositories {
+	mavenLocal()
+	jcenter()
+	mavenCentral()
+}
+
+dependencies {
+	runtime  'org.glassfish.jersey.containers:jersey-container-servlet:2.23.2'
+	compile  'com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.9.2'
+	compileOnly  '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'
+}
+
+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()
+		maven { url "https://plugins.gradle.org/m2/" }
+	}
+	dependencies { classpath 'gradle.plugin.io.github.divinespear:jpa-schema-gradle-plugin:0.3.2' }
+}
+
+jar {
+	metaInf { from '../NOTICE.txt' }
+	metaInf { from '../LICENSE.txt' }
+}
\ No newline at end of file
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..60dbd2f
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/boundary/PreferenceResource.java
@@ -0,0 +1,152 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..dc5f1e6
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/controller/PreferenceService.java
@@ -0,0 +1,232 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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..f0f2f34
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/Preference.java
@@ -0,0 +1,136 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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..3e8c727
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/PreferenceList.java
@@ -0,0 +1,69 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..893e865
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/java/org/eclipse/mdm/preferences/entity/PreferenceMessage.java
@@ -0,0 +1,119 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+

+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..365f9bd
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/main/resources/META-INF/persistence.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- 
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+ -->
+<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..a056e81
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/test/java/org/eclipse/mdm/preferences/controller/PreferenceServiceTest.java
@@ -0,0 +1,233 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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 emgr = factory.createEntityManager();
+		emgr.getTransaction().begin();
+		emgr.createQuery("delete from Preference").executeUpdate();
+		for (Preference p : preferences) {
+			emgr.persist(p);
+		}
+
+		emgr.getTransaction().commit();
+		emgr.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..375d3d6
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/test/java/org/eclipse/mdm/preferences/entity/PreferenceTest.java
@@ -0,0 +1,121 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+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..78e431d
--- /dev/null
+++ b/org.eclipse.mdm.preferences/src/test/resources/META-INF/persistence-test.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- 
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+ -->
+<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/build.gradle b/org.eclipse.mdm.property/build.gradle
new file mode 100644
index 0000000..128e44e
--- /dev/null
+++ b/org.eclipse.mdm.property/build.gradle
@@ -0,0 +1,33 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+description = 'MDM property component'
+
+apply plugin: 'java'
+apply plugin: 'maven'
+apply plugin: 'eclipse'
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+
+repositories {
+	mavenLocal()
+	mavenCentral()
+}
+
+jar {
+	metaInf { from '../NOTICE.txt' }
+	metaInf { from '../LICENSE.txt' }
+}
\ 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
new file mode 100644
index 0000000..8923aae
--- /dev/null
+++ b/org.eclipse.mdm.property/src/main/configuration/global.properties
@@ -0,0 +1,46 @@
+#----------------------------------

+#configuration for Freetext Search

+#----------------------------------

+

+# The URL is required to ask for search results and put documents to

+# The port 9200 is ElasticSearch's default port for the RESTful API

+elasticsearch.url = http://localhost:9200/

+

+# Specifies the maximum number of results per source returned by the query endpoint.

+businessobjects.query.maxresultspersource=1001

+

+

+# ------------------------------

+# configuration for freetextindexer

+# ------------------------------

+# Whether the FreetextSearch should be active or not (true|false). The default value is false.

+# If the parameter is set to false. It cannot be overwritten in the service.xml

+freetext.active=false

+

+# All other parameters for freetextindexer can be overwritten by their datasource specific values from service.xml.

+# For avalon the nameservice and servicename of the ODS service is used.

+

+#Avalon: How often the server is polled for new notifications

+freetext.pollingInterval=5000

+

+#Peak: The URL where the notification service is running. 

+#If running multiple ODS servers this parameter should be overwritten in service.xml

+freetext.notificationUrl=http://localhost:8089/api

+

+#Peak: a name for the registration. This ensures that missed notifications are delivered

+#after reregistration 

+freetext.notificationName=someUniqueName

+

+

+# ------------------------------

+# configuration for file release process (PAK2RAW and PAK2ATFX)

+# ------------------------------

+

+#filerelease.converter.raw.pakInputEntity=<ENTITY FOR PAK INPUT PATH FOR RAW CONVERTER>

+#filerelease.converter.raw.pakInputAttribute=<ATTRIBUTE FOR PAK INPUT PATH FOR RAW CONVERTER>

+#filerelease.converter.pak.pakInputEntity=<ENTITY FOR PAK INPUT PATH FOR ATFX CONVERTER>

+#filerelease.converter.pak.pakInputAttribute=<ATTRIBUTE FOR PAK INPUT PATH FOR ATFX CONVERTER>

+#filerelease.converter.pak.pakApplicationPath=<PATH TO PAK APPLICATION (EXE)>

+#filerelease.converter.pak.modelTypeEntity=<ENTITY FOR PAK MODEL DEFINITION FOR RAW CONVERTER>

+#filerelease.converter.pak.modelTypeAttribute=<ATTRIBUTE FOR PAK MODEL DEFINITION FOR RAW CONVERTER>

+#filerelease.converter.target.root.d
\ No newline at end of file
diff --git a/org.eclipse.mdm.property/src/main/java/META-INF/MANIFEST.MF b/org.eclipse.mdm.property/src/main/java/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..5e94951
--- /dev/null
+++ b/org.eclipse.mdm.property/src/main/java/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0

+Class-Path: 

+

diff --git a/org.eclipse.mdm.property/src/main/java/org/eclipse/mdm/property/GlobalProperty.java b/org.eclipse.mdm.property/src/main/java/org/eclipse/mdm/property/GlobalProperty.java
new file mode 100644
index 0000000..a7696bf
--- /dev/null
+++ b/org.eclipse.mdm.property/src/main/java/org/eclipse/mdm/property/GlobalProperty.java
@@ -0,0 +1,37 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.property;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.enterprise.util.Nonbinding;
+import javax.inject.Qualifier;
+
+/**
+ * Qualifier for global properties.
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@Qualifier
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE })
+public @interface GlobalProperty {
+	@Nonbinding
+	String value() default "";
+}
diff --git a/org.eclipse.mdm.property/src/main/java/org/eclipse/mdm/property/GlobalPropertyException.java b/org.eclipse.mdm.property/src/main/java/org/eclipse/mdm/property/GlobalPropertyException.java
new file mode 100644
index 0000000..0de6d1a
--- /dev/null
+++ b/org.eclipse.mdm.property/src/main/java/org/eclipse/mdm/property/GlobalPropertyException.java
@@ -0,0 +1,52 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+package org.eclipse.mdm.property;
+
+/**
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+public class GlobalPropertyException extends RuntimeException {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 8975069381247044049L;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param error
+	 *            error message.
+	 */
+	public GlobalPropertyException(String error) {
+		super(error);
+	}
+
+	/**
+	 * Constructor
+	 * 
+	 * @param error
+	 *            error message
+	 * @param e
+	 *            throwable
+	 */
+	public GlobalPropertyException(String error, Exception e) {
+		super(error, e);
+	}
+
+}
diff --git a/org.eclipse.mdm.property/src/main/java/org/eclipse/mdm/property/GlobalPropertyService.java b/org.eclipse.mdm.property/src/main/java/org/eclipse/mdm/property/GlobalPropertyService.java
new file mode 100644
index 0000000..99039df
--- /dev/null
+++ b/org.eclipse.mdm.property/src/main/java/org/eclipse/mdm/property/GlobalPropertyService.java
@@ -0,0 +1,133 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+package org.eclipse.mdm.property;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.annotation.PostConstruct;
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.spi.InjectionPoint;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Producer for injectable properties.
+ * 
+ * @author Sebastian Dirsch, Gigatronik Ingolstadt GmbH
+ *
+ */
+@ApplicationScoped
+public class GlobalPropertyService {
+
+	private static final Logger LOG = LoggerFactory.getLogger(GlobalPropertyService.class);
+
+	private static final String COMPONENT_CONFIG_ROOT_FOLDER = "org.eclipse.mdm.property";
+	private static final String GLOBAL_PROPERTIES_FILENAME = "global.properties";
+
+	private Properties globalProperties = new Properties();
+
+	/**
+	 * Reads the properties from the configuration file.
+	 */
+	@PostConstruct
+	public void readProperties() {
+
+		InputStream isGlobalProperties = null;
+
+		try {
+			File componentConfigFolder = new File(COMPONENT_CONFIG_ROOT_FOLDER);
+
+			if (!componentConfigFolder.exists() || !componentConfigFolder.isDirectory()) {
+				LOG.warn("property configuration folder  at '" + componentConfigFolder.getAbsolutePath()
+						+ "' does not exist! No properties available!");
+				return;
+			}
+
+			File globalConfigFile = new File(componentConfigFolder, GLOBAL_PROPERTIES_FILENAME);
+			if (!componentConfigFolder.exists()) {
+				LOG.warn("property configuration file for global properties at '" + globalConfigFile.getAbsolutePath()
+						+ "' does not exist! no global properties available");
+				return;
+			}
+
+			isGlobalProperties = new FileInputStream(globalConfigFile);
+			this.globalProperties.load((isGlobalProperties));
+
+		} catch (IOException e) {
+			LOG.error(e.getMessage(), e);
+			throw new GlobalPropertyException(e.getMessage(), e);
+		} finally {
+			closeInputStream(isGlobalProperties);
+		}
+	}
+
+	/**
+	 * Producer method for global properties.
+	 * 
+	 * @param ip
+	 *            The injection point.
+	 * @return The global property value.
+	 * @throws GlobalPropertyException
+	 *             Thrown if the property is not set.
+	 */
+	@Produces
+	@GlobalProperty
+	public String getGlobalPropertyValue(InjectionPoint ip) throws GlobalPropertyException {
+		GlobalProperty property = ip.getAnnotated().getAnnotation(GlobalProperty.class);
+		String propertyName = property.value();
+		if (!globalProperties.containsKey(propertyName)) {
+			LOG.warn("global property with name '" + propertyName + "' not found!");
+			return "";
+		}
+		return globalProperties.getProperty(propertyName);
+	}
+	
+	/**
+	 * Producer method for a global property map.
+	 * 
+	 * @param ip
+	 *            The injection point.
+	 * @return A map with the global properties 
+	 */
+	@Produces
+	@GlobalProperty
+	public Map<String, String> getGlobalPropertyMap(InjectionPoint ip) {
+		Map<String, String> map = new HashMap<>();
+		for (String key : globalProperties.stringPropertyNames()) {
+			map.put(key, globalProperties.getProperty(key));
+		}
+		return map;
+	}
+
+	private void closeInputStream(InputStream is) {
+		try {
+			if (is != null) {
+				is.close();
+			}
+		} catch (IOException e) {
+			LOG.warn(e.getMessage(), e);
+			throw new GlobalPropertyException(e.getMessage(), e);
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..113254d
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,35 @@
+/********************************************************************************

+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation

+ *

+ * See the NOTICE file(s) distributed with this work for additional

+ * information regarding copyright ownership.

+ *

+ * This program and the accompanying materials are made available under the

+ * terms of the Eclipse Public License v. 2.0 which is available at

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

+ *

+ * SPDX-License-Identifier: EPL-2.0

+ *

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

+
+
+rootProject.name = 'org.eclipse.mdm.nucleus'
+
+def includeIfExists(String projectPath) {
+    if (file(projectPath).exists()) {
+        includeBuild projectPath
+    }
+}
+
+includeIfExists '../org.eclipse.mdm.api.base'
+includeIfExists '../org.eclipse.mdm.api.default'
+includeIfExists '../org.eclipse.mdm.api.odsadapter'
+
+include 'org.eclipse.mdm.application'
+include 'org.eclipse.mdm.connector'
+include 'org.eclipse.mdm.businessobjects'
+include 'org.eclipse.mdm.filerelease'
+include 'org.eclipse.mdm.property'
+include 'org.eclipse.mdm.freetextindexer'
+include 'org.eclipse.mdm.preferences'
+
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
new file mode 100644
index 0000000..edd3b62
--- /dev/null
+++ b/src/main/resources/logback.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<configuration debug="true" scan="true">

+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">

+        <file>${com.sun.aas.instanceRoot}/logs/mdm5.log</file>

+        <append>true</append>

+        <encoder>

+            <Pattern>%date [%thread] %-5level %logger{36} - %msg%n</Pattern>

+        </encoder>

+    </appender>

+    <root>

+        <level value="INFO"/>

+        <appender-ref ref="FILE"/>

+    </root>

+</configuration>
\ No newline at end of file