Merge branch 'dev'
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..11ecb79
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,198 @@
+Eclipse Public License - v 1.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 code and documentation
+   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
+   additions to the Program which: (i) are separate modules of software
+   distributed in conjunction with the Program under their own license
+   agreement, and (ii) are not derivative works of the Program.
+
+"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,
+including all Contributors.
+
+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, in source code and object code form.
+  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 and
+     object code 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.
+
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under its
+own license agreement, provided that:
+
+  a) it complies with the terms and conditions of this Agreement; and
+  b) its license agreement:
+      i) effectively disclaims on behalf of all 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 Contributors all liability for
+         damages, including direct, indirect, special, incidental and
+         consequential damages, such as lost profits;
+    iii) states that any provisions which differ from this Agreement are offered
+         by that Contributor alone and not by any other party; and
+     iv) states that source code for the Program is available from such
+         Contributor, and informs licensees how to obtain it in a reasonable
+         manner on or through a medium customarily used for software exchange.
+
+When the Program is made available in source code form:
+
+  a) it must be made available under this Agreement; and
+  b) a copy of this Agreement must be included with each copy of the Program.
+     Contributors may not remove or alter any copyright notices contained within
+     the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, if
+any, in a manner that reasonably allows subsequent Recipients to identify the
+originator of the Contribution.
+
+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, 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, 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.
+
+This Agreement is governed by the laws of the State of New York and the
+intellectual property laws of the United States of America. No party to this
+Agreement will bring a legal action under this Agreement more than one year
+after the cause of action arose. Each party waives its rights to a jury trial in
+any resulting litigation.
diff --git a/build.gradle b/build.gradle
index 27d74ee..0b08a36 100644
--- a/build.gradle
+++ b/build.gradle
@@ -50,6 +50,10 @@
 	}
 }
 
+configurations {
+    all*.exclude group: 'javax.inject', module: 'javax.inject'
+}
+
 dependencies {
 	compile 'javax:javaee-api:7.0'
 
@@ -67,13 +71,10 @@
 	compile 'com.google.protobuf:protobuf-java:3.0.0-beta-3'
 	compile 'com.google.protobuf:protobuf-java-util:3.0.0-beta-3'
 	compile 'com.google.guava:guava:18.0'
-	compile 'org.glassfish.jersey.core:jersey-client:2.23.1'
-	compile 'org.glassfish.jersey.media:jersey-media-sse:2.23.1'
+	compile 'org.glassfish.jersey.core:jersey-client:2.23.2'
+	compile 'org.glassfish.jersey.media:jersey-media-sse:2.23.2'
 	generatedCompile 'com.google.protobuf:protobuf-java:3.0.0-beta-3'
 
-	// Avalon notification service
-	compile 'org.jacorb:jacorb-services:3.8'
-
 	// querying es
 	compile 'commons-httpclient:commons-httpclient:3.1'
 	compile 'org.apache.commons:commons-lang3:3.4'
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSContext.java b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSContext.java
index 03874c1..d924057 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSContext.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSContext.java
@@ -12,9 +12,12 @@
 
 import org.asam.ods.AoException;
 import org.asam.ods.AoSession;
+import org.asam.ods.ElemId;
+import org.asam.ods.InstanceElement;
 import org.eclipse.mdm.api.base.ConnectionException;
 import org.eclipse.mdm.api.base.adapter.ModelManager;
 import org.eclipse.mdm.api.base.file.FileService;
+import org.eclipse.mdm.api.base.model.Entity;
 import org.eclipse.mdm.api.base.notification.NotificationService;
 import org.eclipse.mdm.api.base.query.DataAccessException;
 import org.eclipse.mdm.api.base.query.QueryService;
@@ -27,9 +30,11 @@
 import org.eclipse.mdm.api.odsadapter.lookup.EntityLoader;
 import org.eclipse.mdm.api.odsadapter.notification.ODSNotificationServiceFactory;
 import org.eclipse.mdm.api.odsadapter.query.ODSEntityFactory;
+import org.eclipse.mdm.api.odsadapter.query.ODSEntityType;
 import org.eclipse.mdm.api.odsadapter.query.ODSModelManager;
 import org.eclipse.mdm.api.odsadapter.query.ODSQueryService;
 import org.eclipse.mdm.api.odsadapter.search.ODSSearchService;
+import org.eclipse.mdm.api.odsadapter.utils.ODSConverter;
 import org.omg.CORBA.ORB;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -156,6 +161,15 @@
 	}
 	
 	/**
+	 * @returns the string "ods"
+	 */
+	@Override
+	public String getAdapterType() {
+		return "ods";
+	}
+	
+	
+	/**
 	 * {@inheritDoc}
 	 */
 	@Override
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSContextFactory.java b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSContextFactory.java
index 3ec55f7..308e591 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSContextFactory.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSContextFactory.java
@@ -8,6 +8,7 @@
 
 package org.eclipse.mdm.api.odsadapter;
 
+import java.util.HashMap;
 import java.util.Map;
 
 import org.asam.ods.AoException;
@@ -107,7 +108,12 @@
 			LOGGER.info("Connection to ODS server established.");
 
 			CORBAFileServerIF fileServer = serviceLocator.resolveFileServer(nameOfService);
-			return new ODSContext(orb, aoSession, fileServer, parameters); 
+		
+			// Create a parameters map without password (which should not be visible from this point onwards),
+			// leaving the original map untouched:
+			Map<String, String> mapParams = new HashMap<>(parameters);
+			mapParams.remove(PARAM_PASSWORD);
+			return new ODSContext(orb, aoSession, fileServer, mapParams); 
 		} catch (AoException e) {
 			closeSession(aoSession);
 			throw new ConnectionException("Unable to connect to ODS server due to: " + e.reason, e);
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
index 20fd266..8b5fbe0 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
@@ -12,6 +12,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumMap;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -19,8 +20,9 @@
 import java.util.stream.Collectors;
 
 import org.asam.ods.AoException;
+import org.asam.ods.ApplicationStructure;
+import org.asam.ods.ElemId;
 import org.asam.ods.InstanceElement;
-import org.eclipse.mdm.api.base.ConnectionException;
 import org.eclipse.mdm.api.base.ServiceNotProvidedException;
 import org.eclipse.mdm.api.base.Transaction;
 import org.eclipse.mdm.api.base.adapter.EntityType;
@@ -45,6 +47,7 @@
 import org.eclipse.mdm.api.odsadapter.filetransfer.Transfer;
 import org.eclipse.mdm.api.odsadapter.lookup.EntityLoader;
 import org.eclipse.mdm.api.odsadapter.lookup.config.EntityConfig.Key;
+import org.eclipse.mdm.api.odsadapter.query.ODSEntityType;
 import org.eclipse.mdm.api.odsadapter.query.ODSModelManager;
 import org.eclipse.mdm.api.odsadapter.transaction.ODSTransaction;
 import org.eclipse.mdm.api.odsadapter.utils.ODSConverter;
@@ -364,4 +367,57 @@
 		}
 	}
 
+	/**
+	 * Retrives the ASAM paths for the given entities. The ASAM paths are prefixed with a servicename, in the form
+	 * <code>corbaloc:[iop|ssliop]:1.2@HOSTNAME:PORT/NameService/MDM.ASAM-ODS/</code> 
+	 * @returns returns a map with the ASAM paths to the given entities. If a entity is not found in the ODS server
+	 * the entity is not included in the result map.
+	 * @throws DataAccessException if links could not be loaded for the given entities
+	 * @throws IllegalArgumentException if a the source or typeName of an entity is invalid
+	 * @see org.eclipse.mdm.api.base.BaseEntityManager#getLinks(Collection)
+	 */
+	@Override
+	public Map<Entity, String> getLinks(Collection<Entity> entities) throws DataAccessException {
+		
+		Map<Entity, String> linkMap = new HashMap<>();
+		
+		ApplicationStructure appStructure;
+		try {
+			appStructure = odsModelManager.getAoSession().getApplicationStructure();
+		} catch (AoException e) {
+			throw new DataAccessException("Could not load application structure! Reason: " + e.reason, e);
+		}
+		
+		String serverRoot = context.getParameters().get(ODSContextFactory.PARAM_NAMESERVICE) 
+				+ "/" + context.getParameters().get(ODSContextFactory.PARAM_SERVICENAME);
+		
+		Map<String, List<Entity>> entitiesByTypeName = entities.stream()
+				.filter(e -> e.getTypeName() != null)
+				.collect(Collectors.groupingBy(Entity::getTypeName));
+		
+		for (Map.Entry<String, List<Entity>> entry : entitiesByTypeName.entrySet()) {
+			ODSEntityType et = (ODSEntityType) odsModelManager.getEntityType(entry.getKey());
+			
+			List<ElemId> elemIds = entry.getValue().stream()
+				.map(e -> new ElemId(et.getODSID(), ODSConverter.toODSLong(Long.parseLong(e.getID()))))
+				.collect(Collectors.toList());
+			
+			try {
+				InstanceElement[] instances = appStructure.getInstancesById(elemIds.toArray(new ElemId[0]));
+				
+				for (InstanceElement ie : instances) {
+					String id = Long.toString(ODSConverter.fromODSLong(ie.getId()));
+					String asamPath = serverRoot + ie.getAsamPath();
+					entry.getValue().stream()
+							.filter(e -> e.getID().equals(id))
+							.findFirst()
+							.ifPresent(e -> linkMap.put(e, asamPath));
+				}
+			} catch (AoException e) {
+				LOGGER.debug("Could not load links for entities: " + entities + ". Reason: " + e.reason, e);
+			}
+		}
+		
+		return linkMap;
+	}
 }
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/filetransfer/CORBAFileServer.java b/src/main/java/org/eclipse/mdm/api/odsadapter/filetransfer/CORBAFileServer.java
index c7cdb3c..9ce663a 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/filetransfer/CORBAFileServer.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/filetransfer/CORBAFileServer.java
@@ -51,18 +51,10 @@
  */
 final class CORBAFileServer {
 
-	// ======================================================================
-	// Class variables
-	// ======================================================================
-
 	private static final Logger LOGGER = LoggerFactory.getLogger(CORBAFileServer.class);
 	private static final int DEFAULT_BUFFER_SIZE = 100_000;
 	private static final int SOCKET_TIMEOUT = 5_000;
 
-	// ======================================================================
-	// Instance variables
-	// ======================================================================
-
 	private final CORBAFileServerIF fileServer;
 	private final AoSession aoSession;
 	private final ORB orb;
@@ -71,10 +63,6 @@
 
 	private final int bufferSize;
 
-	// ======================================================================
-	// Constructors
-	// ======================================================================
-
 	/**
 	 * Constructor.
 	 *
@@ -96,11 +84,7 @@
 
 		bufferSize = getBufferSize();
 	}
-
-	// ======================================================================
-	// Public methods
-	// ======================================================================
-
+	
 	/**
 	 * Opens a consumable download {@link InputStream} for given
 	 * {@link FileLink}.
@@ -188,10 +172,6 @@
 		}
 	}
 
-	// ======================================================================
-	// Public methods
-	// ======================================================================
-
 	/**
 	 * Opens a simple {@link InputStreamIF} using the {@link CORBAFileServerIF}.
 	 *
@@ -236,7 +216,7 @@
 					 * registration is done asynchronously!
 					 */
 					fileServer.getForInstanceBySocket(aoSession, fileLink.getRemotePath(), elemId.aid, elemId.iid,
-							InetAddress.getLocalHost().getHostName(), serverSocket.getLocalPort());
+							InetAddress.getLocalHost().getHostAddress(), serverSocket.getLocalPort());
 				} catch (CORBAFileServerException | IOException e) {
 					LOGGER.error("Unable to initialize socket stream, awaiting socket timeout.", e);
 				}
@@ -305,7 +285,7 @@
 
 			try {
 				return fileServer.saveForInstanceBySocket(aoSession, fileLink.getFileName(), "", elemId.aid, elemId.iid,
-						InetAddress.getLocalHost().getHostName(), serverSocket.getLocalPort());
+						InetAddress.getLocalHost().getHostAddress(), serverSocket.getLocalPort());
 			} catch (CORBAFileServerException e) {
 				throw new IOException("Unable to upload file via socket due to: " + e.reason, e);
 			}
@@ -324,31 +304,19 @@
 			// try to use the same buffer size as the corba file server for best
 			// performance
 			return Integer.parseInt(fileServer.getContext(aoSession, "CORBAFileServer.BufferSize"));
-		} catch (CORBAFileServerException e) {
+		} catch (NumberFormatException | CORBAFileServerException e) {
 			return DEFAULT_BUFFER_SIZE;
 		}
 	}
 
-	// ======================================================================
-	// Inner classes
-	// ======================================================================
-
 	/**
 	 * A simple {@link InputStream} adapter implementation for an
 	 * {@link InputStreamIF}.
 	 */
 	private static final class InputStreamAdapter extends InputStream {
 
-		// ======================================================================
-		// Instance variables
-		// ======================================================================
-
 		private final InputStreamIF inputStream;
 
-		// ======================================================================
-		// Constructors
-		// ======================================================================
-
 		/**
 		 * Constructor.
 		 *
@@ -358,11 +326,7 @@
 		private InputStreamAdapter(InputStreamIF inputStream) {
 			this.inputStream = inputStream;
 		}
-
-		// ======================================================================
-		// Public methods
-		// ======================================================================
-
+		
 		/**
 		 * {@inheritDoc}
 		 */
@@ -412,20 +376,12 @@
 	 */
 	private static final class CORBAInputStreamAdapter extends InputStreamIFPOA implements AutoCloseable {
 
-		// ======================================================================
-		// Instance variables
-		// ======================================================================
-
 		private final InputStream inputStream;
 		private final long length;
 
 		private final byte[] objectID;
 		private final POA poa;
 
-		// ======================================================================
-		// Constructors
-		// ======================================================================
-
 		/**
 		 * Constructor.
 		 *
@@ -452,10 +408,6 @@
 			}
 		}
 
-		// ======================================================================
-		// Public methods
-		// ======================================================================
-
 		/**
 		 * {@inheritDoc}
 		 */
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/lookup/config/EntityConfigRepository.java b/src/main/java/org/eclipse/mdm/api/odsadapter/lookup/config/EntityConfigRepository.java
index b48f46a..d032b59 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/lookup/config/EntityConfigRepository.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/lookup/config/EntityConfigRepository.java
@@ -119,7 +119,7 @@
 
 		EntityConfig<?> config = contextConfigs.get(entityType.getName());
 		if (config == null) {
-			new IllegalArgumentException("Entity configuration for type '" + entityType + "' not found.");
+			throw new IllegalArgumentException("Entity configuration for type '" + entityType + "' not found.");
 		}
 
 		// config is either a context component or context sensor type
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/ODSNotificationServiceFactory.java b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/ODSNotificationServiceFactory.java
index 3f64d9b..57be20d 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/ODSNotificationServiceFactory.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/ODSNotificationServiceFactory.java
@@ -9,8 +9,6 @@
 import org.eclipse.mdm.api.base.notification.NotificationService;
 import org.eclipse.mdm.api.base.query.QueryService;
 import org.eclipse.mdm.api.dflt.ApplicationContext;
-import org.eclipse.mdm.api.odsadapter.ODSContextFactory;
-import org.eclipse.mdm.api.odsadapter.notification.avalon.AvalonNotificationManager;
 import org.eclipse.mdm.api.odsadapter.notification.peak.PeakNotificationManager;
 import org.eclipse.mdm.api.odsadapter.query.ODSModelManager;
 import org.slf4j.Logger;
@@ -61,24 +59,25 @@
 			}
 		} else if (SERVER_TYPE_AVALON.equalsIgnoreCase(type)) {
 
-			String serviceName = getParameter(parameters, ODSContextFactory.PARAM_SERVICENAME);
-			serviceName = serviceName.replace(".ASAM-ODS", "");
-			String nameServiceURL = getParameter(parameters, ODSContextFactory.PARAM_NAMESERVICE);
-
-			LOGGER.info("Connecting to Avalon Notification Server ...");
-			LOGGER.info("Name service URL: {}", nameServiceURL);
-			LOGGER.info("Service name: {}", serviceName);
-
-			long pollingInterval = 500L;
-			try {
-				pollingInterval = Long.parseLong(getParameter(parameters, PARAM_POLLING_INTERVAL));
-			} catch (NumberFormatException | ConnectionException e) {
-				LOGGER.warn("Could not parse parse parameter pollingInterval. Using default value: " + pollingInterval,
-						e);
-			}
-
-			return new AvalonNotificationManager((ODSModelManager) mm, queryService, serviceName, nameServiceURL, true,
-					pollingInterval);
+			throw new IllegalArgumentException("Avalon notification service is not supported yet.");
+//			String serviceName = getParameter(parameters, ODSContextFactory.PARAM_SERVICENAME);
+//			serviceName = serviceName.replace(".ASAM-ODS", "");
+//			String nameServiceURL = getParameter(parameters, ODSContextFactory.PARAM_NAMESERVICE);
+//
+//			LOGGER.info("Connecting to Avalon Notification Server ...");
+//			LOGGER.info("Name service URL: {}", nameServiceURL);
+//			LOGGER.info("Service name: {}", serviceName);
+//
+//			long pollingInterval = 500L;
+//			try {
+//				pollingInterval = Long.parseLong(getParameter(parameters, PARAM_POLLING_INTERVAL));
+//			} catch (NumberFormatException | ConnectionException e) {
+//				LOGGER.warn("Could not parse parse parameter pollingInterval. Using default value: " + pollingInterval,
+//						e);
+//			}
+//
+//			return new AvalonNotificationManager((ODSModelManager) mm, queryService, serviceName, nameServiceURL, true,
+//					pollingInterval);
 		} else {
 			throw new ConnectionException("Invalid server type. Expected on of: 'peak'");
 		}
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/avalon/AvalonNotificationManager.java b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/avalon/AvalonNotificationManager.java
index 6233afc..d30c966 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/avalon/AvalonNotificationManager.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/avalon/AvalonNotificationManager.java
@@ -1,182 +1,182 @@
-package org.eclipse.mdm.api.odsadapter.notification.avalon;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import org.asam.ods.T_LONGLONG;
-import org.eclipse.mdm.api.base.adapter.EntityType;
-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.User;
-import org.eclipse.mdm.api.base.notification.NotificationException;
-import org.eclipse.mdm.api.base.notification.NotificationFilter;
-import org.eclipse.mdm.api.base.notification.NotificationFilter.ModificationType;
-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.base.query.QueryService;
-import org.eclipse.mdm.api.odsadapter.lookup.config.EntityConfig.Key;
-import org.eclipse.mdm.api.odsadapter.notification.NotificationEntityLoader;
-import org.eclipse.mdm.api.odsadapter.query.ODSModelManager;
-import org.eclipse.mdm.api.odsadapter.utils.ODSConverter;
-import org.omg.CORBA.ORB;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.highqsoft.avalonCorbaNotification.notification.MODE_DELETE;
-import com.highqsoft.avalonCorbaNotification.notification.MODE_INSERT;
-import com.highqsoft.avalonCorbaNotification.notification.MODE_MODIFYRIGHTS;
-import com.highqsoft.avalonCorbaNotification.notification.MODE_REPLACE;
-
-/**
- * Notification manager for handling notifications from the Avalon Notification
- * Service
- * 
- * ModificationType.MODEL_MODIFIED is not supported!
- * 
- * @since 1.0.0
- * @author Matthias Koller, Peak Solution GmbH
- *
- */
-public class AvalonNotificationManager implements NotificationService {
-
-	private static final Logger LOGGER = LoggerFactory.getLogger(AvalonNotificationManager.class);
-
-	private final Map<String, EventProcessor> eventProcessors = new HashMap<>();
-
-	private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
-
-	private final ODSModelManager modelManager;
-	private final String serviceName;
-	private final String nameServiceURL;
-	private long pollingInterval = 500L;
-	private final NotificationEntityLoader loader;
-
-	private final ORB orb = ORB.init(new String[] {}, System.getProperties());
-
-	/**
-	 * Creates a new AvalonNotificationManager.
-	 * 
-	 * @param modelManager
-	 *            ODSModelManager used to laod entities.
-	 * @param serviceName
-	 *            name of the notification service.
-	 * @param nameServiceURL
-	 *            URL of the name service.
-	 * @param loadContextDescribable
-	 *            if true, notifications for {@link ContextRoot} and
-	 *            {@link ContextComponent} will load their parent
-	 *            {@link ContextDescribable}.
-	 * @param pollingInterval
-	 *            polling interval in milleseconds
-	 */
-	public AvalonNotificationManager(ODSModelManager modelManager, QueryService queryService, String serviceName, String nameServiceURL,
-			boolean loadContextDescribable, long pollingInterval) {
-		this.modelManager = modelManager;
-		this.serviceName = serviceName;
-		this.nameServiceURL = nameServiceURL;
-		this.pollingInterval = pollingInterval;
-		loader = new NotificationEntityLoader(modelManager, queryService, loadContextDescribable);
-	}
-
-	@Override
-	public void register(String registration, NotificationFilter filter, final NotificationListener listener)
-			throws NotificationException {
-		try {
-			EventProcessor consumer = new EventProcessor(orb, listener, this, nameServiceURL, serviceName);
-
-			List<String> aids = filter.getEntityTypes().stream().map(e -> e.getId()).collect(Collectors.toList());
-
-			Set<ModificationType> modes = filter.getTypes().stream()
-					.filter(m -> !ModificationType.MODEL_MODIFIED.equals(m)).collect(Collectors.toSet());
-
-			consumer.connect();
-			consumer.setFilter(aids, modes);
-
-			ScheduledFuture<?> future = executor.scheduleAtFixedRate(consumer, 0, pollingInterval,
-					TimeUnit.MILLISECONDS);
-			consumer.setFuture(future);
-
-			eventProcessors.put(registration, consumer);
-		} catch (Exception e) {
-			throw new NotificationException("Exception creating notification listener registration!", e);
-		}
-	}
-
-	@Override
-	public void deregister(String registration) {
-		EventProcessor processor = eventProcessors.get(registration);
-		if (processor != null) {
-			processor.disconnect();
-			eventProcessors.remove(registration);
-		}
-	}
-
-	@Override
-	public void close(boolean isDeregisterAll) throws NotificationException {
-		LOGGER.info("Closing NotificationManager...");
-
-		for (String registration : eventProcessors.keySet()) {
-			LOGGER.debug("Disconnecting registration '" + registration + "'.");
-			deregister(registration);
-		}
-
-		try {
-			executor.shutdown();
-			boolean terminated = executor.awaitTermination(10, TimeUnit.SECONDS);
-			if (!terminated) {
-				throw new NotificationException("Could not close all registrations!");
-			}
-		} catch (InterruptedException e) {
-			throw new NotificationException("Could not close all registrations!", e);
-		}
-	}
-
-	void processException(Exception e) {
-		LOGGER.error("Exception during notification processing!", e);
-	}
-
-	void processNotification(short mode, T_LONGLONG aeId, T_LONGLONG ieId, T_LONGLONG userId, String timestamp,
-			NotificationListener notificationListener) {
-
-		try {
-			User user = loader.load(new Key<>(User.class), Long.toString(ODSConverter.fromODSLong(userId)));
-			LOGGER.debug("User loaded");
-
-			EntityType entityType = modelManager.getEntityType(Long.toString(ODSConverter.fromODSLong(aeId)));
-			List<String> ids = Arrays.asList(Long.toString(ODSConverter.fromODSLong(ieId)));
-
-			if (LOGGER.isTraceEnabled()) {
-				LOGGER.trace("Notification event with: entityType=" + entityType + ", user=" + user);
-			}
-
-			switch (mode) {
-			case MODE_INSERT.value:
-				notificationListener.instanceCreated(loader.loadEntities(entityType, ids), user);
-				break;
-			case MODE_REPLACE.value:
-				notificationListener.instanceModified(loader.loadEntities(entityType, ids), user);
-				break;
-			case MODE_DELETE.value:
-				notificationListener.instanceDeleted(entityType, ids, user);
-				break;
-			case MODE_MODIFYRIGHTS.value:
-				notificationListener.securityModified(entityType, ids, user);
-				break;
-			default:
-				processException(new NotificationException("Invalid notification type!"));
-			}
-		} catch (DataAccessException e) {
-			processException(new NotificationException("Cannot load data for notification!", e));
-		}
-	}
-}
+//package org.eclipse.mdm.api.odsadapter.notification.avalon;
+//
+//import java.util.Arrays;
+//import java.util.HashMap;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.Set;
+//import java.util.concurrent.Executors;
+//import java.util.concurrent.ScheduledExecutorService;
+//import java.util.concurrent.ScheduledFuture;
+//import java.util.concurrent.TimeUnit;
+//import java.util.stream.Collectors;
+//
+//import org.asam.ods.T_LONGLONG;
+//import org.eclipse.mdm.api.base.adapter.EntityType;
+//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.User;
+//import org.eclipse.mdm.api.base.notification.NotificationException;
+//import org.eclipse.mdm.api.base.notification.NotificationFilter;
+//import org.eclipse.mdm.api.base.notification.NotificationFilter.ModificationType;
+//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.base.query.QueryService;
+//import org.eclipse.mdm.api.odsadapter.lookup.config.EntityConfig.Key;
+//import org.eclipse.mdm.api.odsadapter.notification.NotificationEntityLoader;
+//import org.eclipse.mdm.api.odsadapter.query.ODSModelManager;
+//import org.eclipse.mdm.api.odsadapter.utils.ODSConverter;
+//import org.omg.CORBA.ORB;
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//
+//import com.highqsoft.avalonCorbaNotification.notification.MODE_DELETE;
+//import com.highqsoft.avalonCorbaNotification.notification.MODE_INSERT;
+//import com.highqsoft.avalonCorbaNotification.notification.MODE_MODIFYRIGHTS;
+//import com.highqsoft.avalonCorbaNotification.notification.MODE_REPLACE;
+//
+///**
+// * Notification manager for handling notifications from the Avalon Notification
+// * Service
+// * 
+// * ModificationType.MODEL_MODIFIED is not supported!
+// * 
+// * @since 1.0.0
+// * @author Matthias Koller, Peak Solution GmbH
+// *
+// */
+//public class AvalonNotificationManager implements NotificationService {
+//
+//	private static final Logger LOGGER = LoggerFactory.getLogger(AvalonNotificationManager.class);
+//
+//	private final Map<String, EventProcessor> eventProcessors = new HashMap<>();
+//
+//	private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
+//
+//	private final ODSModelManager modelManager;
+//	private final String serviceName;
+//	private final String nameServiceURL;
+//	private long pollingInterval = 500L;
+//	private final NotificationEntityLoader loader;
+//
+//	private final ORB orb = ORB.init(new String[] {}, System.getProperties());
+//
+//	/**
+//	 * Creates a new AvalonNotificationManager.
+//	 * 
+//	 * @param modelManager
+//	 *            ODSModelManager used to laod entities.
+//	 * @param serviceName
+//	 *            name of the notification service.
+//	 * @param nameServiceURL
+//	 *            URL of the name service.
+//	 * @param loadContextDescribable
+//	 *            if true, notifications for {@link ContextRoot} and
+//	 *            {@link ContextComponent} will load their parent
+//	 *            {@link ContextDescribable}.
+//	 * @param pollingInterval
+//	 *            polling interval in milleseconds
+//	 */
+//	public AvalonNotificationManager(ODSModelManager modelManager, QueryService queryService, String serviceName, String nameServiceURL,
+//			boolean loadContextDescribable, long pollingInterval) {
+//		this.modelManager = modelManager;
+//		this.serviceName = serviceName;
+//		this.nameServiceURL = nameServiceURL;
+//		this.pollingInterval = pollingInterval;
+//		loader = new NotificationEntityLoader(modelManager, queryService, loadContextDescribable);
+//	}
+//
+//	@Override
+//	public void register(String registration, NotificationFilter filter, final NotificationListener listener)
+//			throws NotificationException {
+//		try {
+//			EventProcessor consumer = new EventProcessor(orb, listener, this, nameServiceURL, serviceName);
+//
+//			List<String> aids = filter.getEntityTypes().stream().map(e -> e.getId()).collect(Collectors.toList());
+//
+//			Set<ModificationType> modes = filter.getTypes().stream()
+//					.filter(m -> !ModificationType.MODEL_MODIFIED.equals(m)).collect(Collectors.toSet());
+//
+//			consumer.connect();
+//			consumer.setFilter(aids, modes);
+//
+//			ScheduledFuture<?> future = executor.scheduleAtFixedRate(consumer, 0, pollingInterval,
+//					TimeUnit.MILLISECONDS);
+//			consumer.setFuture(future);
+//
+//			eventProcessors.put(registration, consumer);
+//		} catch (Exception e) {
+//			throw new NotificationException("Exception creating notification listener registration!", e);
+//		}
+//	}
+//
+//	@Override
+//	public void deregister(String registration) {
+//		EventProcessor processor = eventProcessors.get(registration);
+//		if (processor != null) {
+//			processor.disconnect();
+//			eventProcessors.remove(registration);
+//		}
+//	}
+//
+//	@Override
+//	public void close(boolean isDeregisterAll) throws NotificationException {
+//		LOGGER.info("Closing NotificationManager...");
+//
+//		for (String registration : eventProcessors.keySet()) {
+//			LOGGER.debug("Disconnecting registration '" + registration + "'.");
+//			deregister(registration);
+//		}
+//
+//		try {
+//			executor.shutdown();
+//			boolean terminated = executor.awaitTermination(10, TimeUnit.SECONDS);
+//			if (!terminated) {
+//				throw new NotificationException("Could not close all registrations!");
+//			}
+//		} catch (InterruptedException e) {
+//			throw new NotificationException("Could not close all registrations!", e);
+//		}
+//	}
+//
+//	void processException(Exception e) {
+//		LOGGER.error("Exception during notification processing!", e);
+//	}
+//
+//	void processNotification(short mode, T_LONGLONG aeId, T_LONGLONG ieId, T_LONGLONG userId, String timestamp,
+//			NotificationListener notificationListener) {
+//
+//		try {
+//			User user = loader.load(new Key<>(User.class), Long.toString(ODSConverter.fromODSLong(userId)));
+//			LOGGER.debug("User loaded");
+//
+//			EntityType entityType = modelManager.getEntityTypeById(Long.toString(ODSConverter.fromODSLong(aeId)));
+//			List<String> ids = Arrays.asList(Long.toString(ODSConverter.fromODSLong(ieId)));
+//
+//			if (LOGGER.isTraceEnabled()) {
+//				LOGGER.trace("Notification event with: entityType=" + entityType + ", user=" + user);
+//			}
+//
+//			switch (mode) {
+//			case MODE_INSERT.value:
+//				notificationListener.instanceCreated(loader.loadEntities(entityType, ids), user);
+//				break;
+//			case MODE_REPLACE.value:
+//				notificationListener.instanceModified(loader.loadEntities(entityType, ids), user);
+//				break;
+//			case MODE_DELETE.value:
+//				notificationListener.instanceDeleted(entityType, ids, user);
+//				break;
+//			case MODE_MODIFYRIGHTS.value:
+//				notificationListener.securityModified(entityType, ids, user);
+//				break;
+//			default:
+//				processException(new NotificationException("Invalid notification type!"));
+//			}
+//		} catch (DataAccessException e) {
+//			processException(new NotificationException("Cannot load data for notification!", e));
+//		}
+//	}
+//}
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/avalon/EventProcessor.java b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/avalon/EventProcessor.java
index 05cdd92..32f5778 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/avalon/EventProcessor.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/avalon/EventProcessor.java
@@ -1,270 +1,270 @@
-package org.eclipse.mdm.api.odsadapter.notification.avalon;
-
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ScheduledFuture;
-import java.util.stream.Collectors;
-
-import org.eclipse.mdm.api.base.notification.NotificationException;
-import org.eclipse.mdm.api.base.notification.NotificationFilter.ModificationType;
-import org.eclipse.mdm.api.base.notification.NotificationListener;
-import org.omg.CORBA.ORB;
-import org.omg.CosNaming.NamingContextExt;
-import org.omg.CosNaming.NamingContextExtHelper;
-import org.omg.CosNotification.EventType;
-import org.omg.CosNotification.StructuredEvent;
-import org.omg.CosNotifyChannelAdmin.ClientType;
-import org.omg.CosNotifyChannelAdmin.EventChannel;
-import org.omg.CosNotifyChannelAdmin.EventChannelHelper;
-import org.omg.CosNotifyChannelAdmin.StructuredProxyPullSupplier;
-import org.omg.CosNotifyChannelAdmin.StructuredProxyPullSupplierHelper;
-import org.omg.CosNotifyComm.InvalidEventType;
-import org.omg.CosNotifyComm.StructuredPullConsumerPOA;
-import org.omg.CosNotifyFilter.ConstraintExp;
-import org.omg.CosNotifyFilter.Filter;
-import org.omg.CosNotifyFilter.FilterFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.highqsoft.avalonCorbaNotification.notification.AvalonNotificationCorbaEvent;
-import com.highqsoft.avalonCorbaNotification.notification.AvalonNotificationCorbaEventHelper;
-
-/**
- * Event processor responsible for receiving avalon events from the notification
- * service and redirect them to the manager.
- * 
- * @since 1.0.0
- * @author Matthias Koller, Peak Solution GmbH
- *
- */
-public class EventProcessor extends StructuredPullConsumerPOA implements Runnable {
-
-	private static final Logger LOGGER = LoggerFactory.getLogger(EventProcessor.class);
-
-	private static final String eventDomainName = "AVALON";
-
-	private final ORB orb;
-	private final NotificationListener listener;
-	private final AvalonNotificationManager manager;
-	private final String nameserviceUrl;
-	private final String serviceName;
-
-	private EventChannel eventChannel;
-	private StructuredProxyPullSupplier proxyPullSupplier;
-
-	private boolean connected = false;
-
-	private ScheduledFuture<?> future;
-
-	/**
-	 * Creates a new event processor.
-	 * 
-	 * @param orb
-	 *            CORBA orb to use
-	 * @param listener
-	 *            notification listener consuming the received events
-	 * @param manager
-	 *            notification manager responsible for processing the events
-	 * @param serviceName
-	 *            service name of the CORBA notification service
-	 */
-	public EventProcessor(ORB orb, NotificationListener listener, AvalonNotificationManager manager,
-			String nameserviceUrl, String serviceName) {
-		this.orb = orb;
-		this.nameserviceUrl = nameserviceUrl;
-		this.listener = listener;
-		this.manager = manager;
-		this.serviceName = String.format("com/highqsoft/avalon/notification/%s.Notification", serviceName);
-	}
-
-	/**
-	 * Connect the event processor to the notification service.
-	 * 
-	 * @throws NotificationException
-	 *             in case the notification service cannot be connected.
-	 */
-	public synchronized void connect() throws NotificationException {
-		if (isConnected()) {
-			return;
-		}
-
-		try {
-			NamingContextExt nc = NamingContextExtHelper.narrow(orb.string_to_object(nameserviceUrl));
-
-			eventChannel = EventChannelHelper.narrow(nc.resolve(nc.to_name(serviceName)));
-
-			proxyPullSupplier = StructuredProxyPullSupplierHelper.narrow(eventChannel.default_consumer_admin()
-					.obtain_notification_pull_supplier(ClientType.STRUCTURED_EVENT, new org.omg.CORBA.IntHolder()));
-
-			proxyPullSupplier.connect_structured_pull_consumer(this._this(orb));
-			connected = true;
-		} catch (Exception e) {
-			throw new NotificationException("Cannot connect to notification service!", e);
-		}
-	}
-
-	/**
-	 * Disconnect the event processor from the notification service.
-	 */
-	public synchronized void disconnect() {
-		if (isConnected()) {
-			if (future != null) {
-				future.cancel(false);
-			}
-
-			proxyPullSupplier = null;
-
-			eventChannel._release();
-			eventChannel = null;
-
-			connected = false;
-		}
-	}
-
-	/**
-	 * @return true if the event processor is connected to the notification
-	 *         service
-	 */
-	public synchronized boolean isConnected() {
-		return connected;
-	}
-
-	/**
-	 * Sets the event filter.
-	 * 
-	 * @param aids
-	 *            List with application element IDs to filter for. Empty list
-	 *            means no all.
-	 * @param modificationTypes
-	 *            Collection of modification types to filter for.
-	 * @throws NotificationException
-	 *             if the filter cannot be set
-	 */
-	public void setFilter(List<String> aids, Set<ModificationType> modificationTypes) throws NotificationException {
-		if (!isConnected()) {
-			throw new IllegalStateException("Cannot set filter when disconnected. Please connect first.");
-		}
-
-		try {
-			FilterFactory filterFactory = eventChannel.default_filter_factory();
-			if (filterFactory == null) {
-				throw new NotificationException("No default filter factory found!");
-			}
-
-			Filter filter = filterFactory.create_filter("EXTENDED_TCL");
-			filter.add_constraints(new ConstraintExp[] {
-					new ConstraintExp(getEventTypes(modificationTypes), getConstraintFilter(aids)) });
-			proxyPullSupplier.add_filter(filter);
-		} catch (Exception e) {
-			throw new NotificationException("Exception when creating filter.", e);
-		}
-	}
-
-	/**
-	 * Sets the ScheduledFuture that will be used to stop the event processor
-	 * task.
-	 * 
-	 * @param future
-	 *            ScheduledFuture
-	 */
-	public void setFuture(ScheduledFuture<?> future) {
-		this.future = future;
-	}
-
-	@Override
-	public synchronized void run() {
-		if (isConnected()) {
-			org.omg.CORBA.BooleanHolder bh = new org.omg.CORBA.BooleanHolder();
-
-			try {
-				LOGGER.trace("Looking for structured events....");
-				// try to pull an event
-				StructuredEvent event = proxyPullSupplier.try_pull_structured_event(bh);
-				if (bh.value) {
-					AvalonNotificationCorbaEvent ev = AvalonNotificationCorbaEventHelper
-							.extract(event.remainder_of_body);
-					manager.processNotification(ev.mode, ev.aeId, ev.ieId, ev.userId, ev.timestamp, listener);
-				} else {
-					LOGGER.trace("No structured events found.");
-				}
-			} catch (Exception e) {
-				manager.processException(e);
-			}
-		} else {
-			LOGGER.warn("Disconnected.");
-			manager.processException(new NotificationException("Not connected"));
-			if (future != null) {
-				future.cancel(false);
-			}
-		}
-	}
-
-	@Override
-	public void disconnect_structured_pull_consumer() {
-		LOGGER.info("Disconnected!");
-		connected = false;
-	}
-
-	@Override
-	public void offer_change(EventType[] added, EventType[] removed) throws InvalidEventType {
-		// TODO Auto-generated method stub
-
-	}
-
-	/**
-	 * Constructs a constraint filter.
-	 * 
-	 * @param aids
-	 *            Application Element IDs used for filtering. Empty list means
-	 *            no filter.
-	 * @return Constraint filter containing the given aids
-	 */
-	private String getConstraintFilter(List<String> aids) {
-		if (aids.isEmpty()) {
-			return "TRUE";
-		} else {
-			return aids.stream().map(aid -> String.format("$.filterable_data(%s) == %s", "ApplicationElement", aid))
-					.collect(Collectors.joining(" or "));
-		}
-	}
-
-	/**
-	 * Converts ModificationTypes in EventTypes.
-	 * 
-	 * @param modificationTypes
-	 * @return Array with EventTypes
-	 */
-	private EventType[] getEventTypes(Set<ModificationType> modificationTypes) {
-		if (modificationTypes.isEmpty()) {
-			return new EventType[0];
-		} else {
-			return modificationTypes.stream().map(s -> new EventType(eventDomainName, toAvalonString(s)))
-					.collect(Collectors.toList()).toArray(new EventType[0]);
-		}
-	}
-
-	/**
-	 * Converts a {@link ModificationType} enum value to a event type name for
-	 * the CORBA notification service.
-	 * 
-	 * @param t
-	 *            a modification type
-	 * @return event type name
-	 */
-	private String toAvalonString(ModificationType t) {
-		switch (t) {
-		case INSTANCE_CREATED:
-			return "INSERT";
-		case INSTANCE_MODIFIED:
-			return "REPLACE";
-		case INSTANCE_DELETED:
-			return "DELETE";
-		case SECURITY_MODIFIED:
-			return "MODIFYRIGHTS";
-		case MODEL_MODIFIED:
-			throw new IllegalArgumentException(t.name() + " not supported!");
-		default:
-			throw new IllegalArgumentException("Invalid enum value!");
-		}
-	}
-}
+//package org.eclipse.mdm.api.odsadapter.notification.avalon;
+//
+//import java.util.List;
+//import java.util.Set;
+//import java.util.concurrent.ScheduledFuture;
+//import java.util.stream.Collectors;
+//
+//import org.eclipse.mdm.api.base.notification.NotificationException;
+//import org.eclipse.mdm.api.base.notification.NotificationFilter.ModificationType;
+//import org.eclipse.mdm.api.base.notification.NotificationListener;
+//import org.omg.CORBA.ORB;
+//import org.omg.CosNaming.NamingContextExt;
+//import org.omg.CosNaming.NamingContextExtHelper;
+//import org.omg.CosNotification.EventType;
+//import org.omg.CosNotification.StructuredEvent;
+//import org.omg.CosNotifyChannelAdmin.ClientType;
+//import org.omg.CosNotifyChannelAdmin.EventChannel;
+//import org.omg.CosNotifyChannelAdmin.EventChannelHelper;
+//import org.omg.CosNotifyChannelAdmin.StructuredProxyPullSupplier;
+//import org.omg.CosNotifyChannelAdmin.StructuredProxyPullSupplierHelper;
+//import org.omg.CosNotifyComm.InvalidEventType;
+//import org.omg.CosNotifyComm.StructuredPullConsumerPOA;
+//import org.omg.CosNotifyFilter.ConstraintExp;
+//import org.omg.CosNotifyFilter.Filter;
+//import org.omg.CosNotifyFilter.FilterFactory;
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//
+//import com.highqsoft.avalonCorbaNotification.notification.AvalonNotificationCorbaEvent;
+//import com.highqsoft.avalonCorbaNotification.notification.AvalonNotificationCorbaEventHelper;
+//
+///**
+// * Event processor responsible for receiving avalon events from the notification
+// * service and redirect them to the manager.
+// * 
+// * @since 1.0.0
+// * @author Matthias Koller, Peak Solution GmbH
+// *
+// */
+//public class EventProcessor extends StructuredPullConsumerPOA implements Runnable {
+//
+//	private static final Logger LOGGER = LoggerFactory.getLogger(EventProcessor.class);
+//
+//	private static final String eventDomainName = "AVALON";
+//
+//	private final ORB orb;
+//	private final NotificationListener listener;
+//	private final AvalonNotificationManager manager;
+//	private final String nameserviceUrl;
+//	private final String serviceName;
+//
+//	private EventChannel eventChannel;
+//	private StructuredProxyPullSupplier proxyPullSupplier;
+//
+//	private boolean connected = false;
+//
+//	private ScheduledFuture<?> future;
+//
+//	/**
+//	 * Creates a new event processor.
+//	 * 
+//	 * @param orb
+//	 *            CORBA orb to use
+//	 * @param listener
+//	 *            notification listener consuming the received events
+//	 * @param manager
+//	 *            notification manager responsible for processing the events
+//	 * @param serviceName
+//	 *            service name of the CORBA notification service
+//	 */
+//	public EventProcessor(ORB orb, NotificationListener listener, AvalonNotificationManager manager,
+//			String nameserviceUrl, String serviceName) {
+//		this.orb = orb;
+//		this.nameserviceUrl = nameserviceUrl;
+//		this.listener = listener;
+//		this.manager = manager;
+//		this.serviceName = String.format("com/highqsoft/avalon/notification/%s.Notification", serviceName);
+//	}
+//
+//	/**
+//	 * Connect the event processor to the notification service.
+//	 * 
+//	 * @throws NotificationException
+//	 *             in case the notification service cannot be connected.
+//	 */
+//	public synchronized void connect() throws NotificationException {
+//		if (isConnected()) {
+//			return;
+//		}
+//
+//		try {
+//			NamingContextExt nc = NamingContextExtHelper.narrow(orb.string_to_object(nameserviceUrl));
+//
+//			eventChannel = EventChannelHelper.narrow(nc.resolve(nc.to_name(serviceName)));
+//
+//			proxyPullSupplier = StructuredProxyPullSupplierHelper.narrow(eventChannel.default_consumer_admin()
+//					.obtain_notification_pull_supplier(ClientType.STRUCTURED_EVENT, new org.omg.CORBA.IntHolder()));
+//
+//			proxyPullSupplier.connect_structured_pull_consumer(this._this(orb));
+//			connected = true;
+//		} catch (Exception e) {
+//			throw new NotificationException("Cannot connect to notification service!", e);
+//		}
+//	}
+//
+//	/**
+//	 * Disconnect the event processor from the notification service.
+//	 */
+//	public synchronized void disconnect() {
+//		if (isConnected()) {
+//			if (future != null) {
+//				future.cancel(false);
+//			}
+//
+//			proxyPullSupplier = null;
+//
+//			eventChannel._release();
+//			eventChannel = null;
+//
+//			connected = false;
+//		}
+//	}
+//
+//	/**
+//	 * @return true if the event processor is connected to the notification
+//	 *         service
+//	 */
+//	public synchronized boolean isConnected() {
+//		return connected;
+//	}
+//
+//	/**
+//	 * Sets the event filter.
+//	 * 
+//	 * @param aids
+//	 *            List with application element IDs to filter for. Empty list
+//	 *            means no all.
+//	 * @param modificationTypes
+//	 *            Collection of modification types to filter for.
+//	 * @throws NotificationException
+//	 *             if the filter cannot be set
+//	 */
+//	public void setFilter(List<String> aids, Set<ModificationType> modificationTypes) throws NotificationException {
+//		if (!isConnected()) {
+//			throw new IllegalStateException("Cannot set filter when disconnected. Please connect first.");
+//		}
+//
+//		try {
+//			FilterFactory filterFactory = eventChannel.default_filter_factory();
+//			if (filterFactory == null) {
+//				throw new NotificationException("No default filter factory found!");
+//			}
+//
+//			Filter filter = filterFactory.create_filter("EXTENDED_TCL");
+//			filter.add_constraints(new ConstraintExp[] {
+//					new ConstraintExp(getEventTypes(modificationTypes), getConstraintFilter(aids)) });
+//			proxyPullSupplier.add_filter(filter);
+//		} catch (Exception e) {
+//			throw new NotificationException("Exception when creating filter.", e);
+//		}
+//	}
+//
+//	/**
+//	 * Sets the ScheduledFuture that will be used to stop the event processor
+//	 * task.
+//	 * 
+//	 * @param future
+//	 *            ScheduledFuture
+//	 */
+//	public void setFuture(ScheduledFuture<?> future) {
+//		this.future = future;
+//	}
+//
+//	@Override
+//	public synchronized void run() {
+//		if (isConnected()) {
+//			org.omg.CORBA.BooleanHolder bh = new org.omg.CORBA.BooleanHolder();
+//
+//			try {
+//				LOGGER.trace("Looking for structured events....");
+//				// try to pull an event
+//				StructuredEvent event = proxyPullSupplier.try_pull_structured_event(bh);
+//				if (bh.value) {
+//					AvalonNotificationCorbaEvent ev = AvalonNotificationCorbaEventHelper
+//							.extract(event.remainder_of_body);
+//					manager.processNotification(ev.mode, ev.aeId, ev.ieId, ev.userId, ev.timestamp, listener);
+//				} else {
+//					LOGGER.trace("No structured events found.");
+//				}
+//			} catch (Exception e) {
+//				manager.processException(e);
+//			}
+//		} else {
+//			LOGGER.warn("Disconnected.");
+//			manager.processException(new NotificationException("Not connected"));
+//			if (future != null) {
+//				future.cancel(false);
+//			}
+//		}
+//	}
+//
+//	@Override
+//	public void disconnect_structured_pull_consumer() {
+//		LOGGER.info("Disconnected!");
+//		connected = false;
+//	}
+//
+//	@Override
+//	public void offer_change(EventType[] added, EventType[] removed) throws InvalidEventType {
+//		// TODO Auto-generated method stub
+//
+//	}
+//
+//	/**
+//	 * Constructs a constraint filter.
+//	 * 
+//	 * @param aids
+//	 *            Application Element IDs used for filtering. Empty list means
+//	 *            no filter.
+//	 * @return Constraint filter containing the given aids
+//	 */
+//	private String getConstraintFilter(List<String> aids) {
+//		if (aids.isEmpty()) {
+//			return "TRUE";
+//		} else {
+//			return aids.stream().map(aid -> String.format("$.filterable_data(%s) == %s", "ApplicationElement", aid))
+//					.collect(Collectors.joining(" or "));
+//		}
+//	}
+//
+//	/**
+//	 * Converts ModificationTypes in EventTypes.
+//	 * 
+//	 * @param modificationTypes
+//	 * @return Array with EventTypes
+//	 */
+//	private EventType[] getEventTypes(Set<ModificationType> modificationTypes) {
+//		if (modificationTypes.isEmpty()) {
+//			return new EventType[0];
+//		} else {
+//			return modificationTypes.stream().map(s -> new EventType(eventDomainName, toAvalonString(s)))
+//					.collect(Collectors.toList()).toArray(new EventType[0]);
+//		}
+//	}
+//
+//	/**
+//	 * Converts a {@link ModificationType} enum value to a event type name for
+//	 * the CORBA notification service.
+//	 * 
+//	 * @param t
+//	 *            a modification type
+//	 * @return event type name
+//	 */
+//	private String toAvalonString(ModificationType t) {
+//		switch (t) {
+//		case INSTANCE_CREATED:
+//			return "INSERT";
+//		case INSTANCE_MODIFIED:
+//			return "REPLACE";
+//		case INSTANCE_DELETED:
+//			return "DELETE";
+//		case SECURITY_MODIFIED:
+//			return "MODIFYRIGHTS";
+//		case MODEL_MODIFIED:
+//			throw new IllegalArgumentException(t.name() + " not supported!");
+//		default:
+//			throw new IllegalArgumentException("Invalid enum value!");
+//		}
+//	}
+//}
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/query/ODSQuery.java b/src/main/java/org/eclipse/mdm/api/odsadapter/query/ODSQuery.java
index 507265e..05db375 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/query/ODSQuery.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/query/ODSQuery.java
@@ -52,6 +52,8 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Strings;
+
 /**
  * ODS implementation of the {@link Query} interface.
  *
@@ -66,8 +68,10 @@
 
 	private static final Logger LOGGER = LoggerFactory.getLogger(ODSQuery.class);
 
-	private static final String GROUPE_NAME = "name";
-	private static final Pattern AGGREGATION_NAME_PATTERN = Pattern.compile("\\S+\\((?<" + GROUPE_NAME + ">\\S+)\\)");
+	private static final String GROUP_NAME = "name";
+	private static final String GROUP_AGGRFUNC = "aggrfunc";
+	private static final Pattern AGGREGATION_NAME_PATTERN = Pattern
+			.compile("(?<" + GROUP_AGGRFUNC + ">\\S+)\\((?<" + GROUP_NAME + ">\\S+)\\)");
 
 	// ======================================================================
 	// Instance variables
@@ -323,6 +327,23 @@
 
 		return aidName;
 	}
+	
+	private static Aggregation getAggregation(String odsAggrFunc) {		  
+		switch (Strings.nullToEmpty(odsAggrFunc).trim().toUpperCase()) {
+		case "COUNT": return Aggregation.COUNT;
+		case "DCOUNT": return Aggregation.DISTINCT_COUNT;
+		case "MIN": return Aggregation.MINIMUM;
+		case "MAX": return Aggregation.MAXIMUM;
+		case "AVG": return Aggregation.AVERAGE;
+		case "STDDEV": return Aggregation.DEVIATION;
+		case "SUM": return Aggregation.SUM;
+		case "DISTINCT": return Aggregation.DISTINCT;
+		default: 
+			throw new IllegalArgumentException("Unsupported aggregate function '" + 
+					Strings.nullToEmpty(odsAggrFunc).trim().toUpperCase() + "'!");
+		}
+		
+	}
 
 	// ======================================================================
 	// Inner classes
@@ -436,9 +457,15 @@
 		private RecordFactory(EntityType entityType, NameValueSeqUnitId[] nvsuis) throws DataAccessException {
 			this.entityType = entityType;
 			for (NameValueSeqUnitId nvsui : nvsuis) {
+				String attributeName = nvsui.valName;
+				Aggregation aggregation = Aggregation.NONE;
 				Matcher matcher = AGGREGATION_NAME_PATTERN.matcher(nvsui.valName);
-				String attributeName = matcher.matches() ? matcher.group(GROUPE_NAME) : nvsui.valName;
-				valueFactories.add(new ValueFactory(entityType.getAttribute(attributeName), nvsui));
+				if (matcher.matches()) {
+					attributeName = matcher.group(GROUP_NAME);
+					aggregation = ODSQuery.getAggregation(matcher.group(GROUP_AGGRFUNC));
+				}
+
+				valueFactories.add(new ValueFactory(entityType.getAttribute(attributeName), aggregation, nvsui));
 			}
 		}
 
@@ -489,10 +516,10 @@
 		 * @throws DataAccessException
 		 *             Thrown on conversion errors.
 		 */
-		private ValueFactory(Attribute attribute, NameValueSeqUnitId nvsui) throws DataAccessException {
+		private ValueFactory(Attribute attribute, Aggregation aggregation, NameValueSeqUnitId nvsui) throws DataAccessException {
 			length = nvsui.value.flag.length;
 			unit = attribute.getUnit();
-			values = ODSConverter.fromODSValueSeq(attribute, unit, nvsui.value);
+			values = ODSConverter.fromODSValueSeq(attribute, aggregation, unit, nvsui.value);
 		}
 
 		// ======================================================================
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/ODSTransaction.java b/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/ODSTransaction.java
index 7a56c1f..ac905c4 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/ODSTransaction.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/ODSTransaction.java
@@ -133,13 +133,12 @@
 
 			List<CatalogSensor> catalogSensors = (List<CatalogSensor>) entitiesByClassType.get(CatalogSensor.class);
 			if (catalogSensors != null) {
-				// TODO avalon 4.3b throws an exception in
-				// AoSession.commintTransaction() if multiple
+				// TODO anehmer on 2017-11-16: avalon 4.3b throws an exception in
+				// AoSession.commitTransaction() if multiple
 				// catalog sensors have been deleted and leaves the application
-				// model in a broken state
-
-				// getCatalogManager().createCatalogSensors(catalogSensors);
-				throw new DataAccessException("CURRENTLY NOT IMPLEMENTED");
+				// model in a broken state. This is also stated in the documentation. This
+				// comment should be removed later.
+				getCatalogManager().createCatalogSensors(catalogSensors);
 			}
 
 			List<CatalogAttribute> catalogAttributes = (List<CatalogAttribute>) entitiesByClassType
@@ -217,7 +216,6 @@
 					getUploadService().upload(filtered, null);
 				}
 			}
-
 			executeStatements(et -> new UpdateStatement(this, et, false), entities);
 		} catch (AoException e) {
 			throw new DataAccessException("Unable to update entities due to: " + e.reason, e);
@@ -251,13 +249,12 @@
 
 			List<CatalogSensor> catalogSensors = (List<CatalogSensor>) entitiesByClassType.get(CatalogSensor.class);
 			if (catalogSensors != null) {
-				// TODO avalon 4.3b throws an exception in
-				// AoSession.commintTransaction() if multiple
+				// TODO anehmer on 2017-11-16: avalon 4.3b throws an exception in
+				// AoSession.commitTransaction() if multiple
 				// catalog sensors have been deleted and leaves the application
-				// model in a broken state
-
-				// getCatalogManager().deleteCatalogSensors(catalogSensors);
-				throw new DataAccessException("CURRENTLY NOT IMPLEMENTED");
+				// model in a broken state. This is also stated in the documentation. This
+				// comment should be removed later.
+				getCatalogManager().deleteCatalogSensors(catalogSensors);
 			}
 
 			List<CatalogAttribute> catalogAttributes = (List<CatalogAttribute>) entitiesByClassType
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/UpdateStatement.java b/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/UpdateStatement.java
index ea597b6..819b6bd 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/UpdateStatement.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/transaction/UpdateStatement.java
@@ -218,13 +218,14 @@
 
 		for (Entry<Class<? extends Deletable>, List<? extends Deletable>> entry : core.getChildrenStore().getCurrent()
 				.entrySet()) {
-			Map<Boolean, List<Entity>> patrition = entry.getValue().stream()
+			Map<Boolean, List<Entity>> partition = entry.getValue()
+					.stream()
 					.collect(Collectors.partitioningBy(e -> ODSUtils.isValidID(e.getID())));
-			List<Entity> virtualEntities = patrition.get(Boolean.TRUE);
+			List<Entity> virtualEntities = partition.get(Boolean.FALSE);
 			if (virtualEntities != null && !virtualEntities.isEmpty()) {
 				childrenToCreate.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()).addAll(virtualEntities);
 			}
-			List<Entity> existingEntities = patrition.get(Boolean.FALSE);
+			List<Entity> existingEntities = partition.get(Boolean.TRUE);
 			if (existingEntities != null && !existingEntities.isEmpty()) {
 				childrenToUpdate.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()).addAll(existingEntities);
 			}
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java b/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java
index 986e6af..c62c05f 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverter.java
@@ -41,9 +41,12 @@
 import org.eclipse.mdm.api.base.model.ScalarType;
 import org.eclipse.mdm.api.base.model.Value;
 import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.Aggregation;
 import org.eclipse.mdm.api.base.query.DataAccessException;
 import org.eclipse.mdm.api.odsadapter.query.ODSAttribute;
 
+import com.google.common.collect.Sets;
+
 /**
  * Utility class for value conversions from/to ODS types.
  *
@@ -91,6 +94,8 @@
 	 *
 	 * @param attribute
 	 *            The {@link Attribute}.
+	 * @param aggregation
+	 *            The {@link Aggregation} used when the values were obtained.
 	 * @param unit
 	 *            The unit name.
 	 * @param odsValueSeq
@@ -99,35 +104,41 @@
 	 * @throws DataAccessException
 	 *             Thrown on conversion errors.
 	 */
-	public static List<Value> fromODSValueSeq(Attribute attribute, String unit, TS_ValueSeq odsValueSeq)
+	public static List<Value> fromODSValueSeq(Attribute attribute, Aggregation aggregation, String unit, TS_ValueSeq odsValueSeq)
 			throws DataAccessException {
 		DataType dataType = odsValueSeq.u.discriminator();
 		short[] flags = odsValueSeq.flag;
 		List<Value> values = new ArrayList<>(flags.length);
 
-		if (((ODSAttribute) attribute).isIdAttribute()) {
+		if (((ODSAttribute) attribute).isIdAttribute() && Sets
+				.immutableEnumSet(Aggregation.MINIMUM, Aggregation.MAXIMUM, Aggregation.DISTINCT, Aggregation.NONE)
+				.contains(aggregation)) {
 			if (DataType.DT_LONGLONG == dataType) {
 				T_LONGLONG[] odsValues = odsValueSeq.u.longlongVal();
 				for (int i = 0; i < flags.length; i++) {
-					values.add(attribute.createValue(unit, flags[i] == 15, Long.toString(fromODSLong(odsValues[i]))));
+					values.add(createValue(attribute, aggregation, DataType.DT_STRING, unit, flags[i] == 15,
+							Long.toString(fromODSLong(odsValues[i]))));
 				}
 				return values;
 			} else if (DataType.DS_LONGLONG == dataType) {
 				T_LONGLONG[][] odsValues = odsValueSeq.u.longlongSeq();
 				for (int i = 0; i < flags.length; i++) {
-					values.add(attribute.createValue(unit, flags[i] == 15, toString(odsValues[i])));
+					values.add(createValue(attribute, aggregation, DataType.DS_STRING, unit, flags[i] == 15,
+							toString(odsValues[i])));
 				}
 				return values;
 			} else if (DataType.DT_LONG == dataType) {
 				int[] odsValues = odsValueSeq.u.longVal();
 				for (int i = 0; i < flags.length; i++) {
-					values.add(attribute.createValue(unit, flags[i] == 15, Integer.toString(odsValues[i])));
+					values.add(createValue(attribute, aggregation, DataType.DT_STRING, unit, flags[i] == 15,
+							Integer.toString(odsValues[i])));
 				}
 				return values;
 			} else if (DataType.DS_LONG == dataType) {
 				int[][] odsValues = odsValueSeq.u.longSeq();
 				for (int i = 0; i < flags.length; i++) {
-					values.add(attribute.createValue(unit, flags[i] == 15, toString(odsValues[i])));
+					values.add(createValue(attribute, aggregation, DataType.DS_STRING, unit, flags[i] == 15,
+							toString(odsValues[i])));
 				}
 				return values;
 			}
@@ -136,149 +147,149 @@
 		if (DataType.DT_STRING == dataType) {
 			String[] odsValues = odsValueSeq.u.stringVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DS_STRING == dataType) {
 			String[][] odsValues = odsValueSeq.u.stringSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DT_DATE == dataType) {
 			String[] odsValues = odsValueSeq.u.dateVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSDate(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSDate(odsValues[i])));
 			}
 		} else if (DataType.DS_DATE == dataType) {
 			String[][] odsValues = odsValueSeq.u.dateSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSDateSeq(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSDateSeq(odsValues[i])));
 			}
 		} else if (DataType.DT_BOOLEAN == dataType) {
 			boolean[] odsValues = odsValueSeq.u.booleanVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DS_BOOLEAN == dataType) {
 			boolean[][] odsValues = odsValueSeq.u.booleanSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DT_BYTE == dataType) {
 			byte[] odsValues = odsValueSeq.u.byteVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DS_BYTE == dataType) {
 			byte[][] odsValues = odsValueSeq.u.byteSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DT_SHORT == dataType) {
 			short[] odsValues = odsValueSeq.u.shortVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DS_SHORT == dataType) {
 			short[][] odsValues = odsValueSeq.u.shortSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DT_LONG == dataType) {
 			int[] odsValues = odsValueSeq.u.longVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DS_LONG == dataType) {
 			int[][] odsValues = odsValueSeq.u.longSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DT_LONGLONG == dataType) {
 			T_LONGLONG[] odsValues = odsValueSeq.u.longlongVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSLong(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSLong(odsValues[i])));
 			}
 		} else if (DataType.DS_LONGLONG == dataType) {
 			T_LONGLONG[][] odsValues = odsValueSeq.u.longlongSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSLongSeq(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSLongSeq(odsValues[i])));
 			}
 		} else if (DataType.DT_FLOAT == dataType) {
 			float[] odsValues = odsValueSeq.u.floatVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DS_FLOAT == dataType) {
 			float[][] odsValues = odsValueSeq.u.floatSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DT_DOUBLE == dataType) {
 			double[] odsValues = odsValueSeq.u.doubleVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DS_DOUBLE == dataType) {
 			double[][] odsValues = odsValueSeq.u.doubleSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DT_BYTESTR == dataType) {
 			byte[][] odsValues = odsValueSeq.u.bytestrVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DS_BYTESTR == dataType) {
 			byte[][][] odsValues = odsValueSeq.u.bytestrSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, odsValues[i]));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, odsValues[i]));
 			}
 		} else if (DataType.DT_COMPLEX == dataType) {
 			T_COMPLEX[] odsValues = odsValueSeq.u.complexVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSFloatComplex(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSFloatComplex(odsValues[i])));
 			}
 		} else if (DataType.DS_COMPLEX == dataType) {
 			T_COMPLEX[][] odsValues = odsValueSeq.u.complexSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSFloatComplexSeq(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSFloatComplexSeq(odsValues[i])));
 			}
 		} else if (DataType.DT_DCOMPLEX == dataType) {
 			T_DCOMPLEX[] odsValues = odsValueSeq.u.dcomplexVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSDoubleComplex(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSDoubleComplex(odsValues[i])));
 			}
 		} else if (DataType.DS_DCOMPLEX == dataType) {
 			T_DCOMPLEX[][] odsValues = odsValueSeq.u.dcomplexSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSDoubleComplexSeq(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSDoubleComplexSeq(odsValues[i])));
 			}
 		} else if (DataType.DT_ENUM == dataType) {
 			int[] odsValues = odsValueSeq.u.enumVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15,
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15,
 						ODSEnumerations.fromODSEnum(attribute.getEnumObj(), odsValues[i])));
 			}
 		} else if (DataType.DS_ENUM == dataType) {
 			int[][] odsValues = odsValueSeq.u.enumSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15,
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15,
 						ODSEnumerations.fromODSEnumSeq(attribute.getEnumObj(), odsValues[i])));
 			}
 		} else if (DataType.DT_EXTERNALREFERENCE == dataType) {
 			T_ExternalReference[] odsValues = odsValueSeq.u.extRefVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSExternalReference(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSExternalReference(odsValues[i])));
 			}
 		} else if (DataType.DS_EXTERNALREFERENCE == dataType) {
 			T_ExternalReference[][] odsValues = odsValueSeq.u.extRefSeq();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSExternalReferenceSeq(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSExternalReferenceSeq(odsValues[i])));
 			}
 		} else if (DataType.DT_BLOB == dataType) {
 			Blob[] odsValues = odsValueSeq.u.blobVal();
 			for (int i = 0; i < flags.length; i++) {
-				values.add(attribute.createValue(unit, flags[i] == 15, fromODSBlob(odsValues[i])));
+				values.add(createValue(attribute, aggregation, dataType, unit, flags[i] == 15, fromODSBlob(odsValues[i])));
 			}
 		} else {
 			throw new DataAccessException("Conversion for ODS data type '" + dataType.toString() + "' does not exist.");
@@ -286,6 +297,44 @@
 
 		return values;
 	}
+	
+	/**
+	 * Creates a {@link Value} from the input.
+	 * 
+	 * @param attribute
+	 *            The {@link Attribute}
+	 * @param aggregation
+	 *            The {@link Aggregation} used when the input values were obtained
+	 * @param dataType
+	 *            The {@link DataType} associated with the input value, evaluated
+	 *            only if aggregation != {@code Aggregation.NONE}, otherwise that of
+	 *            the attribute is used
+	 * @param unit
+	 *            The unit of the input value
+	 * @param valid
+	 *            The validity flag of the input value
+	 * @param input
+	 *            The input value
+	 * @return The {@link Value} created.
+	 */
+	private static Value createValue(Attribute attribute, Aggregation aggregation, DataType dataType, String unit,
+			boolean valid, Object input) {
+		if (Aggregation.NONE == aggregation) {
+			return attribute.createValue(unit, valid, input);
+		} else {
+			ValueType<?> valueType = ODSUtils.VALUETYPES.revert(dataType);
+			if (valueType.isEnumerationType()
+					&& attribute.getValueType().isEnumerationType()
+					&& Sets.immutableEnumSet(Aggregation.MINIMUM, Aggregation.MAXIMUM, Aggregation.DISTINCT)
+							.contains(aggregation)) {
+				return valueType.create(String.format("%s(%s)", aggregation.name(), attribute.getName()), unit, valid,
+						input, attribute.getEnumObj().getName());
+			} else {
+				return valueType.create(String.format("%s(%s)", aggregation.name(), attribute.getName()), unit, valid,
+						input);
+			}
+		}
+	}
 
 	private static String[] toString(int[] odsValues) {
 		return IntStream.of(odsValues).mapToObj(Integer::toString).toArray(String[]::new);
diff --git a/src/test/java/org/eclipse/mdm/api/odsadapter/AggregationTest.java b/src/test/java/org/eclipse/mdm/api/odsadapter/AggregationTest.java
new file mode 100644
index 0000000..89a8f8b
--- /dev/null
+++ b/src/test/java/org/eclipse/mdm/api/odsadapter/AggregationTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2018 Peak Solution GmbH
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.eclipse.mdm.api.odsadapter;
+
+import static org.assertj.core.api.Assertions.assertThat;
+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.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.mdm.api.base.ConnectionException;
+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.Unit;
+import org.eclipse.mdm.api.base.model.ValueType;
+import org.eclipse.mdm.api.base.query.Aggregation;
+import org.eclipse.mdm.api.base.query.DataAccessException;
+import org.eclipse.mdm.api.base.query.QueryService;
+import org.eclipse.mdm.api.base.query.Result;
+import org.eclipse.mdm.api.dflt.ApplicationContext;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+
+/**
+ * Aggregation test
+ *
+ * @since 1.0.0
+ * @author maf, Peak Solution GmbH
+ */
+@Ignore
+// FIXME 11.01.2018: this test needs a running ODS Server, that is not suitable for continous build in Jenkins.
+// Comment this in for local tests only.
+public class AggregationTest {
+
+	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;
+
+	@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);
+	}
+
+	@AfterClass
+	public static void tearDownAfterClass() throws ConnectionException {
+		if (context != null) {
+			context.close();
+		}
+	}
+	
+	@org.junit.Test
+	public void testQueryIdAndNameNoAggregation() throws DataAccessException {
+		ModelManager modelManager = context.getModelManager().get();
+		QueryService queryService = context.getQueryService().get();
+
+		EntityType unitEntityType = modelManager.getEntityType(Unit.class);
+		Attribute idAttribute = unitEntityType.getAttribute("Id");
+		Attribute nameAttribute = unitEntityType.getAttribute("Name");
+		
+		List<Result> listRes = queryService.createQuery()
+				.select(idAttribute)
+				.select(nameAttribute)
+				.fetch();
+
+		assertThat(listRes.size()).isGreaterThanOrEqualTo(1);
+		assertThat(listRes.get(0).getValue(idAttribute).getValueType()).isEqualTo(ValueType.STRING);
+		// Test retrieving attribute value with Aggregation.NONE (should yield the same result as with no aggregation):
+		assertThat(listRes.get(0).getValue(idAttribute, Aggregation.NONE).getValueType()).isEqualTo(ValueType.STRING);
+		assertThat(listRes.get(0).getValue(nameAttribute).getValueType()).isEqualTo(ValueType.STRING);
+	}
+	
+	@org.junit.Test
+	public void testQueryIdWithAggregation() throws DataAccessException {
+		ModelManager modelManager = context.getModelManager().get();
+		QueryService queryService = context.getQueryService().get();
+
+		EntityType unitEntityType = modelManager.getEntityType(Unit.class);
+		Attribute idAttribute = unitEntityType.getAttribute("Id");
+		
+		List<Result> listRes = queryService.createQuery()
+				.select(idAttribute, Aggregation.MAXIMUM) // should be a string in result, just like non-aggregated Id attribute
+				.select(idAttribute, Aggregation.AVERAGE) // should be a numeric value in result
+				.fetch();
+
+		assertThat(listRes.size()).isGreaterThanOrEqualTo(1);
+		assertThat(listRes.get(0).getValue(idAttribute, Aggregation.MAXIMUM).getValueType()).isEqualTo(ValueType.STRING);
+		assertThat(listRes.get(0).getValue(idAttribute, Aggregation.AVERAGE).getValueType()).isEqualTo(ValueType.LONG);
+	}
+	
+	@org.junit.Test
+	public void testQueryFactorWithAggregation() throws DataAccessException {
+		ModelManager modelManager = context.getModelManager().get();
+		QueryService queryService = context.getQueryService().get();
+
+		EntityType unitEntityType = modelManager.getEntityType(Unit.class);
+		Attribute factorAttribute = unitEntityType.getAttribute("Factor");
+		
+		List<Result> listRes = queryService.createQuery()
+				.select(factorAttribute, Aggregation.COUNT)
+				.group(factorAttribute)
+				.fetch();
+		
+		assertThat(listRes.size()).isGreaterThanOrEqualTo(1);
+		assertThat(listRes.get(0).getValue(factorAttribute, Aggregation.COUNT).getValueType()).isEqualTo(ValueType.INTEGER);
+	}
+	
+	@org.junit.Test
+	public void testQueryFactorWithAndWithoutAggregation() throws DataAccessException {
+		ModelManager modelManager = context.getModelManager().get();
+		QueryService queryService = context.getQueryService().get();
+
+		EntityType unitEntityType = modelManager.getEntityType(Unit.class);
+		Attribute factorAttribute = unitEntityType.getAttribute("Factor");
+		
+		List<Result> listRes = queryService.createQuery()
+				.select(factorAttribute)
+				.select(factorAttribute, Aggregation.SUM)
+				.group(factorAttribute)
+				.fetch();
+
+		
+		assertThat(listRes.size()).isGreaterThanOrEqualTo(1);
+		assertThat(listRes.get(0).getValue(factorAttribute).getValueType()).isEqualTo(ValueType.DOUBLE);
+		assertThat(listRes.get(0).getValue(factorAttribute, Aggregation.SUM).getValueType()).isEqualTo(ValueType.DOUBLE);
+	}
+
+}
diff --git a/src/test/java/org/eclipse/mdm/api/odsadapter/ODSContextTest.java b/src/test/java/org/eclipse/mdm/api/odsadapter/ODSContextTest.java
new file mode 100644
index 0000000..cafc650
--- /dev/null
+++ b/src/test/java/org/eclipse/mdm/api/odsadapter/ODSContextTest.java
@@ -0,0 +1,171 @@
+/*******************************************************************************

+  * Copyright (c) 2018 Peak Solution GmbH

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

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

+  * which accompanies this distribution, and is available at

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

+  *

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

+package org.eclipse.mdm.api.odsadapter;

+

+import static org.assertj.core.api.Assertions.assertThat;

+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 static org.mockito.Mockito.when;

+

+import java.util.Arrays;

+import java.util.HashMap;

+import java.util.Map;

+

+import org.asam.ods.AoException;

+import org.asam.ods.InstanceElement;

+import org.assertj.core.api.Condition;

+import org.assertj.core.api.Fail;

+import org.eclipse.mdm.api.base.ConnectionException;

+import org.eclipse.mdm.api.base.ServiceNotProvidedException;

+import org.eclipse.mdm.api.base.model.BaseEntity;

+import org.eclipse.mdm.api.base.model.Entity;

+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.odsadapter.utils.ODSConverter;

+import org.junit.AfterClass;

+import org.junit.BeforeClass;

+import org.junit.Ignore;

+import org.mockito.Mockito;

+

+@Ignore

+//FIXME 26.01.2018: this test needs a running ODS Server, that is not suitable for continous build in Jenkins.

+//Comment this in for local tests only.

+public class ODSContextTest {

+

+	/*

+	 * 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 EntityManager entityManager;

+

+	private static String nameServiceHost = System.getProperty("host");

+	private static String nameServicePort = System.getProperty("port");

+	private static String serviceName = System.getProperty("service");

+

+	

+	@BeforeClass

+	public static void setUpBeforeClass() throws ConnectionException {

+

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

+		entityManager = context.getEntityManager()

+				.orElseThrow(() -> new ServiceNotProvidedException(EntityManager.class));

+	}

+

+	@AfterClass

+	public static void tearDownAfterClass() throws ConnectionException {

+		if (context != null) {

+			context.close();

+		}

+	}

+	

+	@org.junit.Test

+	public void testGetAdapterType() {

+		assertThat(context.getAdapterType()).isEqualTo("ods");

+	}

+	

+	@org.junit.Test

+	public void testGetLink() {

+		// we assume a test with ID 4 exists.

+		Test test = entityManager.load(Test.class, "4");

+		

+		Map<Entity, String> asamPaths = entityManager.getLinks(Arrays.asList(test));

+		

+		assertThat(asamPaths)

+			.hasSize(1)

+			.containsOnlyKeys(test)

+			.hasEntrySatisfying(test, new Condition<String>(s -> s.startsWith(getLinkPrefix()), ""));

+	}

+	

+	@org.junit.Test

+	public void testGetLinkAndLoadOdsInstance() {

+		// we assume a test with ID 4 exists.

+		long testId = 4L;

+		Test test = entityManager.load(Test.class, Long.toString(testId));

+		

+		Map<Entity, String> asamPaths = entityManager.getLinks(Arrays.asList(test));

+		

+		assertThat(asamPaths)

+			.hasSize(1)

+			.containsOnlyKeys(test)

+			.hasEntrySatisfying(test, new Condition<String>(s -> s.startsWith(getLinkPrefix()), ""));

+		// We try to load the instance from the ODS Server with the AsamPath, but without the service prefix

+		String asamPathWithoutService = asamPaths.get(test).replace(getLinkPrefix(), "");

+		

+		try {

+			InstanceElement ie = ((ODSContext) context).getAoSession().getApplicationStructure().getInstanceByAsamPath(asamPathWithoutService);

+			assertThat(ODSConverter.fromODSLong(ie.getId())).isEqualTo(testId);

+		} catch (AoException e) {

+			Fail.fail("Instance with AsamPath '" + asamPathWithoutService + "' could not be loaded. Reason: " + e.reason, e);

+		}

+	}

+	

+	@org.junit.Test(expected=IllegalArgumentException.class)

+	public void testGetLinksWithInvalidType() {

+		// we assume a test with ID 4 exists.

+		Test test = entityManager.load(Test.class, "4");

+		BaseEntity entity = Mockito.mock(BaseEntity.class);

+		// Mock an entity with a non existing TypeName

+		when(entity.getTypeName()).thenReturn("invalidType");

+		

+		entityManager.getLinks(Arrays.asList(test, entity));

+	}

+	

+	@org.junit.Test

+	public void testGetLinksWithEnitityWithMissingId() {

+		// we assume a test with ID 4 exists.

+		Test test = entityManager.load(Test.class, "4");

+		

+		// Mock an entity with a non existing ID

+		TestStep testStep = Mockito.mock(TestStep.class);

+		when(testStep.getID()).thenReturn("-1");

+		

+		Map<Entity, String> asamPaths = entityManager.getLinks(Arrays.asList(test, testStep));

+		

+		assertThat(asamPaths)

+			.hasSize(1)

+			.containsOnlyKeys(test)

+			.hasEntrySatisfying(test, new Condition<String>(s -> s.startsWith(getLinkPrefix()), ""));

+	}

+	

+	private String getLinkPrefix() {

+		return String.format("corbaloc::1.2@%s:%s/NameService/%s.ASAM-ODS", nameServiceHost, nameServicePort, serviceName);

+	}

+}

diff --git a/src/test/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverterTest.java b/src/test/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverterTest.java
index 2478683..c8234d2 100644
--- a/src/test/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverterTest.java
+++ b/src/test/java/org/eclipse/mdm/api/odsadapter/utils/ODSConverterTest.java
@@ -16,6 +16,7 @@
 
 import org.asam.ods.TS_UnionSeq;
 import org.asam.ods.TS_ValueSeq;
+import org.eclipse.mdm.api.base.query.Aggregation;
 import org.eclipse.mdm.api.odsadapter.query.ODSAttribute;
 import org.junit.Test;
 
@@ -25,7 +26,7 @@
 	public void testFromODSValueSeqODSDateYear() throws Exception {
 		ODSAttribute attr = mock(ODSAttribute.class);
 
-		ODSConverter.fromODSValueSeq(attr, "", getTS_ValueSeqFromDates("2017"));
+		ODSConverter.fromODSValueSeq(attr, Aggregation.NONE, "", getTS_ValueSeqFromDates("2017"));
 
 		verify(attr).createValue(eq(""), eq(true), eq(LocalDateTime.of(2017, 1, 1, 0, 0)));
 	}
@@ -34,7 +35,7 @@
 	public void testFromODSValueSeqODSDateMonth() throws Exception {
 		ODSAttribute attr = mock(ODSAttribute.class);
 
-		ODSConverter.fromODSValueSeq(attr, "", getTS_ValueSeqFromDates("201710"));
+		ODSConverter.fromODSValueSeq(attr, Aggregation.NONE, "", getTS_ValueSeqFromDates("201710"));
 
 		verify(attr).createValue(eq(""), eq(true), eq(LocalDateTime.of(2017, 10, 1, 0, 0)));
 	}
@@ -43,7 +44,7 @@
 	public void testFromODSValueSeqODSDate() throws Exception {
 		ODSAttribute attr = mock(ODSAttribute.class);
 
-		ODSConverter.fromODSValueSeq(attr, "", getTS_ValueSeqFromDates("20171004"));
+		ODSConverter.fromODSValueSeq(attr, Aggregation.NONE, "", getTS_ValueSeqFromDates("20171004"));
 
 		verify(attr).createValue(eq(""), eq(true), eq(LocalDateTime.of(2017, 10, 4, 0, 0)));
 	}
@@ -52,7 +53,7 @@
 	public void testFromODSValueSeqODSDateHour() throws Exception {
 		ODSAttribute attr = mock(ODSAttribute.class);
 
-		ODSConverter.fromODSValueSeq(attr, "", getTS_ValueSeqFromDates("2017100412"));
+		ODSConverter.fromODSValueSeq(attr, Aggregation.NONE, "", getTS_ValueSeqFromDates("2017100412"));
 
 		verify(attr).createValue(eq(""), eq(true), eq(LocalDateTime.of(2017, 10, 4, 12, 0)));
 	}
@@ -61,7 +62,7 @@
 	public void testFromODSValueSeqODSDateMinute() throws Exception {
 		ODSAttribute attr = mock(ODSAttribute.class);
 
-		ODSConverter.fromODSValueSeq(attr, "", getTS_ValueSeqFromDates("201710041213"));
+		ODSConverter.fromODSValueSeq(attr, Aggregation.NONE, "", getTS_ValueSeqFromDates("201710041213"));
 
 		verify(attr).createValue(eq(""), eq(true), eq(LocalDateTime.of(2017, 10, 4, 12, 13)));
 	}
@@ -70,7 +71,7 @@
 	public void testFromODSValueSeqODSDateSecond() throws Exception {
 		ODSAttribute attr = mock(ODSAttribute.class);
 
-		ODSConverter.fromODSValueSeq(attr, "", getTS_ValueSeqFromDates("20171004121314"));
+		ODSConverter.fromODSValueSeq(attr, Aggregation.NONE, "", getTS_ValueSeqFromDates("20171004121314"));
 
 		verify(attr).createValue(eq(""), eq(true), eq(LocalDateTime.of(2017, 10, 4, 12, 13, 14, 0)));
 	}
@@ -79,7 +80,7 @@
 	public void testFromODSValueSeqODSDateMillisecond() throws Exception {
 		ODSAttribute attr = mock(ODSAttribute.class);
 
-		ODSConverter.fromODSValueSeq(attr, "", getTS_ValueSeqFromDates("20171004121314123"));
+		ODSConverter.fromODSValueSeq(attr, Aggregation.NONE, "", getTS_ValueSeqFromDates("20171004121314123"));
 
 		verify(attr).createValue(eq(""), eq(true), eq(LocalDateTime.of(2017, 10, 4, 12, 13, 14, 123_000_000)));
 	}
@@ -88,14 +89,14 @@
 	public void testFromODSValueSeqInvalidLength() throws Exception {
 		ODSAttribute attr = mock(ODSAttribute.class);
 
-		ODSConverter.fromODSValueSeq(attr, "", getTS_ValueSeqFromDates("201710041"));
+		ODSConverter.fromODSValueSeq(attr, Aggregation.NONE, "", getTS_ValueSeqFromDates("201710041"));
 	}
 
 	@Test(expected = IllegalArgumentException.class)
 	public void testFromODSValueSeqInvalidMonth() throws Exception {
 		ODSAttribute attr = mock(ODSAttribute.class);
 
-		ODSConverter.fromODSValueSeq(attr, "", getTS_ValueSeqFromDates("20171304"));
+		ODSConverter.fromODSValueSeq(attr, Aggregation.NONE, "", getTS_ValueSeqFromDates("20171304"));
 	}
 
 	private TS_ValueSeq getTS_ValueSeqFromDates(String... dates) {