Merge branch 'dev' into mkoller/atfxadapter

Conflicts:
	build.gradle


Signed-off-by: Matthias Koller <m.koller@peak-solution.de>
diff --git a/build.gradle b/build.gradle
index 3cb1c8c..e67b14e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -14,7 +14,7 @@
 
 description = 'MDM API - ODSAdapter'
 group = 'org.eclipse.mdm'
-version = '5.1.0M6-SNAPSHOT'
+version = '5.1.0M6-ATFX-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 f01c859..527a186 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/ODSEntityManager.java
@@ -44,6 +44,8 @@
 import org.eclipse.mdm.api.base.model.Environment;
 import org.eclipse.mdm.api.base.model.MeasuredValues;
 import org.eclipse.mdm.api.base.model.StatusAttachable;
+import org.eclipse.mdm.api.base.model.Test;
+import org.eclipse.mdm.api.base.model.TestStep;
 import org.eclipse.mdm.api.base.model.User;
 import org.eclipse.mdm.api.base.query.DataAccessException;
 import org.eclipse.mdm.api.base.query.Filter;
@@ -55,9 +57,12 @@
 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.dflt.model.TemplateTest;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStep;
 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.ODSEntityFactory;
 import org.eclipse.mdm.api.odsadapter.query.ODSEntityType;
 import org.eclipse.mdm.api.odsadapter.query.ODSModelManager;
 import org.eclipse.mdm.api.odsadapter.transaction.ODSTransaction;
@@ -443,4 +448,20 @@
 
 		return linkMap;
 	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Optional<TemplateTest> loadTemplate(Test test) {
+		return Optional.of(ODSEntityFactory.extract(test).getMutableStore().get(TemplateTest.class));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Optional<TemplateTestStep> loadTemplate(TestStep testStep) {
+		return Optional.of(ODSEntityFactory.extract(testStep).getMutableStore().get(TemplateTestStep.class));
+	}
 }
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/lookup/config/DefaultEntityConfigRepositoryLoader.java b/src/main/java/org/eclipse/mdm/api/odsadapter/lookup/config/DefaultEntityConfigRepositoryLoader.java
index 6eeed7c..82529f5 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/lookup/config/DefaultEntityConfigRepositoryLoader.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/lookup/config/DefaultEntityConfigRepositoryLoader.java
@@ -39,9 +39,13 @@
 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.Role;
+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;
@@ -148,16 +152,29 @@
 		templateTestStepConfig.setComparator(Versionable.COMPARATOR);
 		entityConfigRepository.register(templateTestStepConfig);
 
-		// Status TestStep
-		// TODO check MIME type genration
-		// entityConfigRepository.register(create(new Key<>(Status.class,
-		// TestStep.class), "StatusTestStep", true));
+		// Status
+		entityConfigRepository.register(create(modelManager, new Key<>(Status.class), "Status", false));
+
+		// ProjectDomain
+		entityConfigRepository.register(create(modelManager, new Key<>(ProjectDomain.class), "ProjectDomain", false));
+
+		// Domain
+		entityConfigRepository.register(create(modelManager, new Key<>(Domain.class), "Domain", false));
+
+		// Classification
+		EntityConfig<Classification> classificationConfig = create(modelManager, new Key<>(Classification.class),
+				"Classification", false);
+		classificationConfig.addOptional(entityConfigRepository.findRoot(new Key<>(Status.class)));
+		classificationConfig.addOptional(entityConfigRepository.findRoot(new Key<>(ProjectDomain.class)));
+		classificationConfig.addOptional(entityConfigRepository.findRoot(new Key<>(Domain.class)));
+		entityConfigRepository.register(classificationConfig);
 
 		// TestStep
 		EntityConfig<TestStep> testStepConfig = create(modelManager, new Key<>(TestStep.class), "TestStep", true);
 		// testStepConfig.addMandatory(entityConfigRepository.findRoot(new
 		// Key<>(Status.class, TestStep.class)));
 		testStepConfig.addOptional(entityConfigRepository.findRoot(new Key<>(TemplateTestStep.class)));
+		testStepConfig.addOptional(entityConfigRepository.findRoot(new Key<>(Classification.class)));
 		testStepConfig.setComparator(Sortable.COMPARATOR);
 		entityConfigRepository.register(testStepConfig);
 
@@ -183,6 +200,7 @@
 		// testConfig.addMandatory(entityConfigRepository.findRoot(new
 		// Key<>(Status.class, Test.class)));
 		testConfig.addOptional(entityConfigRepository.findRoot(new Key<>(TemplateTest.class)));
+		testConfig.addOptional(entityConfigRepository.findRoot(new Key<>(Classification.class)));
 		entityConfigRepository.register(testConfig);
 
 		// ContextRoots
@@ -296,7 +314,7 @@
 	/**
 	 * Creates a new {@link EntityConfig}.
 	 *
-	 * @param <T>        The entity type.
+	 * @param            <T> The entity type.
 	 * @param key        Used as identifier.
 	 * @param typeName   Name of the associated {@link EntityType}.
 	 * @param appendName Flag indicates whether to append the entity types base name
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/peak/EventProcessor.java b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/peak/EventProcessor.java
index 25b0f49..010d535 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/peak/EventProcessor.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/peak/EventProcessor.java
@@ -43,7 +43,8 @@
 	private PeakNotificationManager odsNotificationManager;
 	private MediaType eventMediaType;
 	private boolean closeInvoked = false;
-
+	private boolean disconnected = false;
+	
 	public EventProcessor(EventInput eventInput, NotificationListener listener,
 			PeakNotificationManager odsNotificationManager, MediaType eventMediaType) {
 		this.eventInput = eventInput;
@@ -64,6 +65,7 @@
 					odsNotificationManager
 							.processException(new NotificationException("Inbound event input stream closed!"));
 				}
+				disconnected = true;
 				return;
 			}
 
@@ -76,9 +78,16 @@
 			} catch (ProcessingException e) {
 				odsNotificationManager
 						.processException(new NotificationException("Cannot deserialize notification event!", e));
+				disconnected = true;
 				return;
 			}
 		}
+		
+		disconnected = true;
+	}
+
+	public boolean isDisconnected() {
+		return disconnected;
 	}
 
 	public void stop() {
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/peak/PeakNotificationManager.java b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/peak/PeakNotificationManager.java
index 5c3976d..37a67b7 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/notification/peak/PeakNotificationManager.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/notification/peak/PeakNotificationManager.java
@@ -18,6 +18,7 @@
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 import javax.ws.rs.client.Client;
@@ -87,6 +88,8 @@
 					.build();
 
 			endpoint = client.target(url).path("events");
+
+			Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> reconnect(), 10, 10, TimeUnit.SECONDS);
 		} catch (Exception e) {
 			throw new NotificationException("Could not create " + PeakNotificationManager.class.getName() + "!", e);
 		}
@@ -123,27 +126,7 @@
 					"Could not create registration at notification service: " + response.readEntity(String.class));
 		}
 
-		try {
-			LOGGER.info("Requesting event input for {}", registration);
-			EventInput eventInput = endpoint.path(registration).request(SseFeature.SERVER_SENT_EVENTS_TYPE)
-					.get(EventInput.class);
-
-			LOGGER.info("Received event input, starting event processor.");
-			EventProcessor processor = new EventProcessor(eventInput, listener, this, MediaType.APPLICATION_JSON_TYPE);
-
-			executor.submit(processor);
-
-			processors.put(registration, processor);
-			LOGGER.info("Event processor started.");
-		} catch (Exception e) {
-			try {
-				deregister(registration);
-			} catch (Exception ex) {
-				LOGGER.error("Exception upon deregistering!");
-			}
-			throw new NotificationException("Could not create event input stream!", e);
-		}
-
+		recreateEventStream(registration, listener);
 	}
 
 	/*
@@ -177,6 +160,55 @@
 		}
 	}
 
+	/**
+	 * Recreates the {@link EventInput}, attaches an {@link EventProcessor} to it
+	 * and stores the {@link EventProcessor} in the processors map.
+	 * 
+	 * @param registration
+	 * @param listener
+	 * @throws NotificationException
+	 */
+	private void recreateEventStream(String registration, NotificationListener listener) throws NotificationException {
+		try {
+			LOGGER.info("Requesting event input for {}", registration);
+			EventInput eventInput = endpoint.path(registration).request(SseFeature.SERVER_SENT_EVENTS_TYPE)
+					.get(EventInput.class);
+
+			LOGGER.info("Received event input, starting event processor.");
+			EventProcessor processor = new EventProcessor(eventInput, listener, this, MediaType.APPLICATION_JSON_TYPE);
+
+			executor.submit(processor);
+
+			processors.put(registration, processor);
+			LOGGER.info("Event processor started.");
+		} catch (Exception e) {
+			try {
+				deregister(registration);
+			} catch (Exception ex) {
+				LOGGER.error("Exception upon deregistering!");
+			}
+			throw new NotificationException("Could not create event input stream!", e);
+		}
+	}
+
+	/**
+	 * Checks if any registered event processor got disconnected and tries to
+	 * recreate the event stream, if necessary.
+	 */
+	private void reconnect() {
+
+		for (Map.Entry<String, EventProcessor> entry : processors.entrySet()) {
+			if (entry.getValue().isDisconnected()) {
+				try {
+					LOGGER.trace("Registration '{}' was disconnected and will be recreated.", entry.getKey());
+					recreateEventStream(entry.getKey(), entry.getValue().getListener());
+				} catch (NotificationException e) {
+					LOGGER.warn("Cannot recreate event stream for registration " + entry.getKey(), e);
+				}
+			}
+		}
+	}
+
 	private void close(String registration) {
 		if (processors.containsKey(registration)) {
 			EventProcessor processor = processors.get(registration);
diff --git a/src/main/java/org/eclipse/mdm/api/odsadapter/search/BaseEntitySearchQuery.java b/src/main/java/org/eclipse/mdm/api/odsadapter/search/BaseEntitySearchQuery.java
index 4f75079..cc2db44 100644
--- a/src/main/java/org/eclipse/mdm/api/odsadapter/search/BaseEntitySearchQuery.java
+++ b/src/main/java/org/eclipse/mdm/api/odsadapter/search/BaseEntitySearchQuery.java
@@ -89,9 +89,10 @@
 		EntityConfig<?> entityConfig = modelManager.getEntityConfig(new Key<>(entityClass));
 		EntityType source = entityConfig.getEntityType();
 
-		entityConfig.getOptionalConfigs().stream().map(EntityConfig::getEntityType).forEach(entityType -> {
-			joinTree.addNode(source, entityType, true, JoinType.OUTER);
-		});
+		entityConfig.getOptionalConfigs().stream().map(EntityConfig::getEntityType)
+				.filter(et -> !"Classification".equals(et.getName())).forEach(entityType -> {
+					joinTree.addNode(source, entityType, true, JoinType.OUTER);
+				});
 
 		entityConfig.getMandatoryConfigs().stream().map(EntityConfig::getEntityType).forEach(entityType -> {
 			joinTree.addNode(source, entityType, true, JoinType.INNER);
@@ -195,9 +196,10 @@
 		joinTree.addNode(modelManager.getEntityType(joinConfig.source), target, joinConfig.viaParent, JoinType.INNER);
 
 		// add target's optional dependencies
-		targetEntityConfig.getOptionalConfigs().stream().map(EntityConfig::getEntityType).forEach(entityType -> {
-			joinTree.addNode(target, entityType, true, JoinType.OUTER);
-		});
+		targetEntityConfig.getOptionalConfigs().stream().map(EntityConfig::getEntityType)
+				.filter(et -> !"Classification".equals(et.getName())).forEach(entityType -> {
+					joinTree.addNode(target, entityType, true, JoinType.OUTER);
+				});
 
 		// add target's mandatory dependencies
 		targetEntityConfig.getMandatoryConfigs().stream().map(EntityConfig::getEntityType).forEach(entityType -> {
diff --git a/src/test/java/org/eclipse/mdm/api/odsadapter/ODSRoleTest.java b/src/test/java/org/eclipse/mdm/api/odsadapter/ODSRoleTest.java
index 4e84e9e..8b2a9da 100644
--- a/src/test/java/org/eclipse/mdm/api/odsadapter/ODSRoleTest.java
+++ b/src/test/java/org/eclipse/mdm/api/odsadapter/ODSRoleTest.java
@@ -36,10 +36,11 @@
 import org.eclipse.mdm.api.dflt.model.Role;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-//@Ignore
+@Ignore
 // FIXME 10.7.2017: 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 ODSRoleTest {