Merge branch 'dev'

Change-Id: Icc746729170e28a6da5e892c8c726fb3991c7d16
diff --git a/build.gradle b/build.gradle
index 9cd5fb0..7f7632b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,7 +14,7 @@
 
 description = 'MDM API - ODSAdapter'
 group = 'org.eclipse.mdm'
-version = '5.0.0'
+version = '5.1.0M1-SNAPSHOT'
 
 apply plugin: 'java'
 apply plugin: 'maven'
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 de1df72..bb7b141 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
@@ -1,16 +1,16 @@
-/********************************************************************************

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

- *

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

- * information regarding copyright ownership.

- *

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

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

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

- *

- * SPDX-License-Identifier: EPL-2.0

- *

- ********************************************************************************/

+/********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
 
 
 package org.eclipse.mdm.api.odsadapter;
@@ -34,15 +34,7 @@
 import org.eclipse.mdm.api.base.Transaction;
 import org.eclipse.mdm.api.base.adapter.EntityType;
 import org.eclipse.mdm.api.base.massdata.ReadRequest;
-import org.eclipse.mdm.api.base.model.Channel;
-import org.eclipse.mdm.api.base.model.ChannelGroup;
-import org.eclipse.mdm.api.base.model.ContextDescribable;
-import org.eclipse.mdm.api.base.model.ContextRoot;
-import org.eclipse.mdm.api.base.model.ContextType;
-import org.eclipse.mdm.api.base.model.Entity;
-import org.eclipse.mdm.api.base.model.Environment;
-import org.eclipse.mdm.api.base.model.MeasuredValues;
-import org.eclipse.mdm.api.base.model.User;
+import org.eclipse.mdm.api.base.model.*;
 import org.eclipse.mdm.api.base.query.DataAccessException;
 import org.eclipse.mdm.api.base.query.Filter;
 import org.eclipse.mdm.api.base.query.JoinType;
@@ -51,6 +43,8 @@
 import org.eclipse.mdm.api.base.query.Record;
 import org.eclipse.mdm.api.base.query.Result;
 import org.eclipse.mdm.api.dflt.EntityManager;
+import org.eclipse.mdm.api.dflt.model.Classification;
+import org.eclipse.mdm.api.dflt.model.Status;
 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;
@@ -80,7 +74,7 @@
 	// Instance variables
 	// ======================================================================
 	private final Transfer transfer = Transfer.SOCKET;
-	
+
 	private final ODSContext context;
 	private final ODSModelManager odsModelManager;
 	private final QueryService queryService;
@@ -199,35 +193,39 @@
 		return entityLoader.loadAll(new Key<>(entityClass), pattern);
 	}
 
-	// /**
-	// * {@inheritDoc}
-	// */
-	// @Override
-	// public <T extends StatusAttachable> List<T> loadAll(Class<T> entityClass,
-	// Status status, String pattern)
-	// throws DataAccessException {
-	// EntityType entityType = modelManager.getEntityType(entityClass);
-	// EntityType statusEntityType =
-	// modelManager.getEntityType(status.getTypeName());
-	//
-	// List<String> instanceIDs = modelManager.createQuery()
-	// .join(entityType, statusEntityType).selectID(entityType)
-	// .fetch(Filter.and()
-	// .id(statusEntityType, status.getID())
-	// .name(entityType, pattern))
-	// .stream().map(r ->
-	// r.getRecord(entityType)).map(Record::getID).collect(Collectors.toList());
-	//
-	// return entityLoader.loadAll(new Key<>(entityClass), instanceIDs);
-	// }
-	//
-	// @Override
-	// public List<Status> loadAllStatus(Class<? extends StatusAttachable>
-	// entityClass, String pattern)
-	// throws DataAccessException {
-	// return entityLoader.loadAll(new Key<>(Status.class, entityClass),
-	// pattern);
-	// }
+	 /**
+	 * {@inheritDoc}
+	 */
+	 @Override
+	 public <T extends StatusAttachable> List<T> loadAll(Class<T> entityClass, Status status, String pattern)
+	    throws DataAccessException {
+	 EntityType entityType = odsModelManager.getEntityType(entityClass);
+	 EntityType classificationType = odsModelManager.getEntityType(Classification.class);
+	 EntityType statusEntityType =
+			 odsModelManager.getEntityType(status.getTypeName());
+
+	 List<String> instanceIDs = queryService.createQuery()
+			 .join(entityType, classificationType)
+			 .join(classificationType, statusEntityType)
+			 .selectID(entityType)
+			 .fetch(Filter.and()
+					 .id(statusEntityType, status.getID())
+					 .name(entityType, pattern))
+			 .stream().map(r ->
+					 r.getRecord(entityType)).map(Record::getID).collect(Collectors.toList());
+
+	 return entityLoader.loadAll(new Key<>(entityClass), instanceIDs);
+	 }
+
+/*
+	 @Override
+	 public List<Status> loadAllStatus(Class<? extends StatusAttachable>
+	 entityClass, String pattern)
+	 throws DataAccessException {
+		 return entityLoader.loadAll(new Key<>(Status.class, entityClass),
+		 pattern);
+	 }
+	 */
 
 	/**
 	 * {@inheritDoc}
@@ -376,7 +374,7 @@
 
 	/**
 	 * 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> 
+	 * <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
@@ -385,33 +383,33 @@
 	 */
 	@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) 
+
+		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();
@@ -424,7 +422,7 @@
 				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 4f0b644..1ebf4ee 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
@@ -1,16 +1,16 @@
-/********************************************************************************

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

- *

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

- * information regarding copyright ownership.

- *

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

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

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

- *

- * SPDX-License-Identifier: EPL-2.0

- *

- ********************************************************************************/

+/********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
 
 
 package org.eclipse.mdm.api.odsadapter.filetransfer;
@@ -20,9 +20,15 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.InetAddress;
+import java.net.NetworkInterface;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
 
+import org.apache.commons.lang3.StringUtils;
 import org.asam.ods.AoSession;
 import org.asam.ods.ElemId;
 import org.eclipse.mdm.api.base.ServiceNotProvidedException;
@@ -61,6 +67,7 @@
 	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;
+	private static final String INTERFACE_NAME_PROPERTY = "org.eclipse.mdm.api.odsadapter.filetransfer.interfaceName";
 
 	private final CORBAFileServerIF fileServer;
 	private final AoSession aoSession;
@@ -73,7 +80,7 @@
 	/**
 	 * Constructor.
 	 *
-	 * @param modelManager
+	 * @param context
 	 *            Used for setup.
 	 * @param transfer
 	 *            The transfer type for up- and downloads.
@@ -83,7 +90,7 @@
 		if (!(mm instanceof ODSModelManager)) {
 			throw new IllegalArgumentException("The supplied ModelManager must be an ODSModelManager!");
 		}
-		
+
 		fileServer = context.getFileServer();
 		aoSession = context.getAoSession();
 		orb = context.getORB();
@@ -91,7 +98,7 @@
 
 		bufferSize = getBufferSize();
 	}
-	
+
 	/**
 	 * Opens a consumable download {@link InputStream} for given
 	 * {@link FileLink}.
@@ -212,7 +219,7 @@
 	 */
 	private InputStream openSocketStream(FileLink fileLink, ElemId elemId) throws IOException {
 		// auto assigned port with awaiting exactly ONE incoming connection
-		try (ServerSocket serverSocket = new ServerSocket(0, 1)) {
+		try (ServerSocket serverSocket = new ServerSocket(0, 1, getInterfaceAddress())) {
 			serverSocket.setSoTimeout(SOCKET_TIMEOUT * 6);
 
 			new Thread(() -> {
@@ -235,6 +242,60 @@
 		}
 	}
 
+	private InetAddress getInterfaceAddress() throws SocketException {
+		String property = System.getProperty(INTERFACE_NAME_PROPERTY);
+		if (StringUtils.isEmpty(property)) {
+			LOGGER.debug("Using no specified interface for file transfer, property not set.");
+			return null;
+		}
+		List<NetworkInterface> interfaces = getInterfaceList();
+		List<NetworkInterface> filteredInterfaces = getFilteredInterfaces(interfaces);
+		for (NetworkInterface filteredInterface : filteredInterfaces) {
+			if (filteredInterface.getName().equals(property)
+					&& filteredInterface.getInetAddresses().hasMoreElements()) {
+				InetAddress inetAddress = filteredInterface.getInetAddresses().nextElement();
+				LOGGER.debug("Using interface {} with address {} for file transfer.", filteredInterface.getName(),
+						inetAddress);
+				return inetAddress;
+			}
+		}
+		return getFallback(filteredInterfaces);
+	}
+
+	private InetAddress getFallback(List<NetworkInterface> filteredInterfaces) {
+		if (filteredInterfaces.isEmpty()) {
+			LOGGER.debug("Using no specified interface for file transfer, property set but no running interface.");
+			return null;
+		}
+		NetworkInterface networkInterface = filteredInterfaces.get(0);
+		Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
+		if (inetAddresses.hasMoreElements()) {
+			InetAddress address = inetAddresses.nextElement();
+			LOGGER.debug("Using interface {} with address {} for file transfer.", networkInterface.getName(), address);
+			return address;
+		}
+		return null;
+	}
+
+	private List<NetworkInterface> getFilteredInterfaces(List<NetworkInterface> interfaces) throws SocketException {
+		List<NetworkInterface> filteredInterfaces = new ArrayList<>();
+		for (NetworkInterface anInterface : interfaces) {
+			if (anInterface.isUp() && !anInterface.isLoopback() && !anInterface.isVirtual()) {
+				filteredInterfaces.add(anInterface);
+			}
+		}
+		return filteredInterfaces;
+	}
+
+	private List<NetworkInterface> getInterfaceList() throws SocketException {
+		List<NetworkInterface> result = new ArrayList<>();
+		Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+		while (networkInterfaces.hasMoreElements()) {
+			result.add(networkInterfaces.nextElement());
+		}
+		return result;
+	}
+
 	/**
 	 * Uploads given {@link InputStream} for given {@link FileLink} using the
 	 * {@link CORBAFileServerIF}.
@@ -274,7 +335,7 @@
 	 */
 	private String uploadVIASocket(InputStream inputStream, FileLink fileLink, ElemId elemId) throws IOException {
 		// auto assigned port with awaiting exactly ONE incoming connection
-		try (ServerSocket serverSocket = new ServerSocket(0, 1)) {
+		try (ServerSocket serverSocket = new ServerSocket(0, 1, getInterfaceAddress())) {
 			serverSocket.setSoTimeout(SOCKET_TIMEOUT * 6);
 
 			new Thread(() -> {
@@ -290,11 +351,14 @@
 				}
 			}).start();
 
+			int localPort = serverSocket.getLocalPort();
+			String localHostName = serverSocket.getInetAddress().getHostName();
 			try {
 				return fileServer.saveForInstanceBySocket(aoSession, fileLink.getFileName(), "", elemId.aid, elemId.iid,
-						InetAddress.getLocalHost().getHostAddress(), serverSocket.getLocalPort());
+						localHostName, localPort);
 			} catch (CORBAFileServerException e) {
-				throw new IOException("Unable to upload file via socket due to: " + e.reason, e);
+				String message = String.format("Unable to upload file via socket to %s:%d due to: %s", localHostName, localPort, e.reason);
+				throw new IOException(message, e);
 			}
 		}
 	}
@@ -333,7 +397,7 @@
 		private InputStreamAdapter(InputStreamIF inputStream) {
 			this.inputStream = inputStream;
 		}
-		
+
 		/**
 		 * {@inheritDoc}
 		 */
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/NotificationEntityLoader.java b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/NotificationEntityLoader.java
index 10521c1..b2f6b92 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/NotificationEntityLoader.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/NotificationEntityLoader.java
@@ -121,12 +121,12 @@
 
 		List<String> testStepIDs = queryService.createQuery().selectID(testStep)
 				.join(testStep.getRelation(contextRoot), JoinType.OUTER)
-				.fetch(Filter.and().add(ComparisonOperator.IN_SET.create(contextRoot.getIDAttribute(), ids)))
+				.fetch(Filter.and().add(ComparisonOperator.IN_SET.create(contextRoot.getIDAttribute(), ids.toArray(new String[0]))))
 				.stream().map(r -> r.getRecord(testStep)).map(Record::getID).collect(Collectors.toList());
 
 		List<String> measurementIDs = queryService.createQuery().selectID(measurement)
 				.join(measurement.getRelation(contextRoot), JoinType.OUTER)
-				.fetch(Filter.and().add(ComparisonOperator.IN_SET.create(contextRoot.getIDAttribute(), ids)))
+				.fetch(Filter.and().add(ComparisonOperator.IN_SET.create(contextRoot.getIDAttribute(), ids.toArray(new String[0]))))
 				.stream().map(r -> r.getRecord(measurement)).map(Record::getID).collect(Collectors.toList());
 
 		List<ContextDescribable> list = new ArrayList<>();
@@ -159,13 +159,13 @@
 		List<String> testStepIDs = queryService.createQuery().selectID(testStep)
 				.join(testStep.getRelation(contextRoot), JoinType.OUTER)
 				.join(contextRoot.getRelation(contextComponent), JoinType.OUTER)
-				.fetch(Filter.and().add(ComparisonOperator.IN_SET.create(contextComponent.getIDAttribute(), ids)))
+				.fetch(Filter.and().add(ComparisonOperator.IN_SET.create(contextComponent.getIDAttribute(), ids.toArray(new String[0]))))
 				.stream().map(r -> r.getRecord(testStep)).map(Record::getID).collect(Collectors.toList());
 
 		List<String> measurementIDs = queryService.createQuery().selectID(measurement)
 				.join(measurement.getRelation(contextRoot), JoinType.OUTER)
 				.join(contextRoot.getRelation(contextComponent), JoinType.OUTER)
-				.fetch(Filter.and().add(ComparisonOperator.IN_SET.create(contextComponent.getIDAttribute(), ids)))
+				.fetch(Filter.and().add(ComparisonOperator.IN_SET.create(contextComponent.getIDAttribute(), ids.toArray(new String[0]))))
 				.stream().map(r -> r.getRecord(measurement)).map(Record::getID).collect(Collectors.toList());
 
 		List<ContextDescribable> list = new ArrayList<>();
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/query/ODSModelManager.java b/src/main/java/org/eclipse/mdm/api/odsadapter/query/ODSModelManager.java
index 1dc169f..6efe7f3 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/query/ODSModelManager.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/query/ODSModelManager.java
@@ -1,16 +1,16 @@
-/********************************************************************************

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

- *

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

- * information regarding copyright ownership.

- *

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

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

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

- *

- * SPDX-License-Identifier: EPL-2.0

- *

- ********************************************************************************/

+/********************************************************************************
+ * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ ********************************************************************************/
 
 
 package org.eclipse.mdm.api.odsadapter.query;
@@ -24,13 +24,10 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.stream.Stream;
 
-import com.google.common.base.Stopwatch;
-import org.apache.commons.lang3.time.StopWatch;
 import org.asam.ods.AIDName;
 import org.asam.ods.AggrFunc;
 import org.asam.ods.AoException;
@@ -75,8 +72,12 @@
 import org.eclipse.mdm.api.dflt.model.CatalogAttribute;
 import org.eclipse.mdm.api.dflt.model.CatalogComponent;
 import org.eclipse.mdm.api.dflt.model.CatalogSensor;
+import org.eclipse.mdm.api.dflt.model.Classification;
+import org.eclipse.mdm.api.dflt.model.Domain;
 import org.eclipse.mdm.api.dflt.model.Pool;
 import org.eclipse.mdm.api.dflt.model.Project;
+import org.eclipse.mdm.api.dflt.model.ProjectDomain;
+import org.eclipse.mdm.api.dflt.model.Status;
 import org.eclipse.mdm.api.dflt.model.TemplateAttribute;
 import org.eclipse.mdm.api.dflt.model.TemplateComponent;
 import org.eclipse.mdm.api.dflt.model.TemplateRoot;
@@ -498,8 +499,27 @@
 		entityConfigRepository.register(create(new Key<>(Pool.class), "StructureLevel", true));
 		entityConfigRepository.register(create(new Key<>(PhysicalDimension.class), "PhysDimension", false));
 		entityConfigRepository.register(create(new Key<>(User.class), "User", false));
-		entityConfigRepository.register(create(new Key<>(Measurement.class), "MeaResult", false));
 		entityConfigRepository.register(create(new Key<>(ChannelGroup.class), "SubMatrix", false));
+		entityConfigRepository.register(create(new Key<>(Status.class), "Status", true));
+
+		// Project Domain
+		EntityConfig<ProjectDomain> projectDomainEntityConfig = create(new Key<>(ProjectDomain.class), "ProjectDomain", true);
+		entityConfigRepository.register(projectDomainEntityConfig);
+
+		// Domain
+		EntityConfig<Domain> domainEntityConfig = create(new Key<>(Domain.class), "Domain", true);
+		entityConfigRepository.register(domainEntityConfig);
+
+		// Classification
+		EntityConfig<Classification> classificationEntityConfig = create(new Key<>(Classification.class), "Classification", true);
+		classificationEntityConfig.addOptional(entityConfigRepository.findRoot(new Key<>(ProjectDomain.class)));
+		classificationEntityConfig.addOptional(entityConfigRepository.findRoot(new Key<>(Domain.class)));
+		classificationEntityConfig.addOptional(entityConfigRepository.findRoot(new Key<>(Status.class)));
+		entityConfigRepository.register(classificationEntityConfig);
+
+		EntityConfig<Measurement> measurementEntityConfig = create(new Key<>(Measurement.class), "MeaResult", false);
+		measurementEntityConfig.addOptional(entityConfigRepository.findRoot(new Key<>(Classification.class)));
+		entityConfigRepository.register(measurementEntityConfig);
 
 		// Unit
 		EntityConfig<Unit> unitConfig = create(new Key<>(Unit.class), "Unit", false);