| /******************************************************************************** |
| * Copyright (c) 2015-2018 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| ********************************************************************************/ |
| |
| package org.eclipse.mdm.businessobjects.service; |
| |
| import static org.eclipse.mdm.businessobjects.control.ContextActivity.CONTEXT_GROUP_MEASURED; |
| import static org.eclipse.mdm.businessobjects.control.ContextActivity.CONTEXT_GROUP_ORDERED; |
| |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.NoSuchElementException; |
| import java.util.stream.Collectors; |
| |
| import javax.ejb.EJB; |
| import javax.ejb.Stateless; |
| import javax.inject.Inject; |
| |
| import org.eclipse.mdm.api.base.Transaction; |
| 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.ContextType; |
| import org.eclipse.mdm.api.base.model.Entity; |
| import org.eclipse.mdm.api.base.model.Environment; |
| import org.eclipse.mdm.api.base.model.Measurement; |
| import org.eclipse.mdm.api.base.model.TestStep; |
| import org.eclipse.mdm.api.dflt.EntityManager; |
| import org.eclipse.mdm.api.dflt.model.EntityFactory; |
| import org.eclipse.mdm.api.dflt.model.TemplateRoot; |
| import org.eclipse.mdm.api.dflt.model.TemplateTestStep; |
| import org.eclipse.mdm.businessobjects.control.ContextActivity; |
| import org.eclipse.mdm.businessobjects.control.MDMEntityAccessException; |
| import org.eclipse.mdm.businessobjects.entity.MDMAttribute; |
| import org.eclipse.mdm.businessobjects.entity.MDMEntity; |
| import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse; |
| import org.eclipse.mdm.businessobjects.utils.Serializer; |
| import org.eclipse.mdm.businessobjects.utils.ServiceUtils; |
| import org.eclipse.mdm.connector.boundary.ConnectorService; |
| |
| import com.google.common.base.Strings; |
| |
| import io.vavr.Lazy; |
| import io.vavr.Value; |
| import io.vavr.collection.HashMap; |
| import io.vavr.collection.List; |
| import io.vavr.collection.Map; |
| import io.vavr.control.Try; |
| |
| /** |
| * Class providing basic data access methods to contexts. |
| * |
| * @author Johannes Stamm, PeakSolution GmbH Nuernberg |
| * |
| */ |
| @Stateless |
| public class ContextService { |
| |
| @Inject |
| private ConnectorService connectorService; |
| |
| @EJB |
| private EntityService entityService; |
| |
| /** |
| * Vavr conform version of contextActivity getTestStepContext function. |
| * |
| * returns the ordered and measurement context for a {@link TestStep}. If no |
| * {@link ContextType}s are defined for this method call, the method returns all |
| * context informations of the available {@link ContextType}s. Otherwise you can |
| * specify a list of {@link ContextType}s. |
| * |
| * Possible {@link ContextType}s are {@link ContextType}.UNITUNDERTEST, |
| * {@link ContextType}.TESTSEQUENCE and {@link ContextType}.TESTEQUIPMENT. |
| * |
| * @param sourceName name of the source (MDM {@link Environment} name) |
| * @param testStepId the id of {@link TestStep} context is looked up for |
| * @param contextTypes list of {@link ContextType}s |
| * @return the ordered and measured context data as context object for the |
| * identified {@link TestStep} |
| */ |
| public Try<Map<String, Map<ContextType, ContextRoot>>> getTestStepContext(String sourceName, String testStepId, |
| ContextType... contextTypes) { |
| return getTestStepContext(sourceName, entityService.find(sourceName, TestStep.class, testStepId), contextTypes); |
| } |
| |
| /** |
| * Vavr conform version of contextActivity getTestStepContext function. |
| * |
| * returns the ordered and measurement context for a {@link TestStep}. If no |
| * {@link ContextType}s are defined for this method call, the method returns all |
| * context informations of the available {@link ContextType}s. Otherwise you can |
| * specify a list of {@link ContextType}s. |
| * |
| * Possible {@link ContextType}s are {@link ContextType}.UNITUNDERTEST, |
| * {@link ContextType}.TESTSEQUENCE and {@link ContextType}.TESTEQUIPMENT. |
| * |
| * @param sourceName name of the source (MDM {@link Environment} name) |
| * @param testStep {@link Try} of the {@link TestStep} |
| * @param contextTypes list of {@link ContextType}s |
| * @return the ordered and measured context data as context object for the |
| * identified {@link TestStep} |
| */ |
| private Try<Map<String, Map<ContextType, ContextRoot>>> getTestStepContext(String sourceName, |
| Try<TestStep> testStep, ContextType... contextTypes) { |
| |
| Try<Map<ContextType, ContextRoot>> contextOrdered = entityService.getEntityManager(sourceName) |
| .map(e -> HashMap.ofAll(e.loadContexts(testStep.get(), contextTypes))); |
| |
| Try<Map<ContextType, ContextRoot>> contextMeasured = entityService.getEntityManager(sourceName).map( |
| e -> HashMap.ofAll(e.loadContexts(findMeasurements(sourceName, testStep).get().get(), contextTypes))); |
| |
| return Try |
| .of(() -> Lazy |
| .of(() -> HashMap.of(CONTEXT_GROUP_ORDERED, |
| contextOrdered.recover(NoSuchElementException.class, t -> HashMap.empty()).get(), |
| CONTEXT_GROUP_MEASURED, |
| contextMeasured.recover(NoSuchElementException.class, t -> HashMap.empty()).get())) |
| .get()); |
| } |
| |
| /** |
| * Vavr conform version of contextActivity getMeasurementContext function. |
| * |
| * returns the ordered and measurement context for a {@link Measurement}. If no |
| * {@link ContextType}s are defined for this method call, the method returns all |
| * context informations of the available {@link ContextType}s. Otherwise you can |
| * specify a list of {@link ContextType}s. |
| * |
| * Possible {@link ContextType}s are {@link ContextType}.UNITUNDERTEST, |
| * {@link ContextType}.TESTSEQUENCE and {@link ContextType}.TESTEQUIPMENT. |
| * |
| * @param sourceName name of the source (MDM {@link Environment} name) |
| * @param measurementId the id of {@link Measurement} context is looked up for |
| * @param contextTypes list of {@link ContextType}s |
| * @return the ordered and measured context data as context object for the |
| * identified {@link Measurement} |
| */ |
| public Try<Map<String, Map<ContextType, ContextRoot>>> getMeasurementContext(String sourceName, |
| String measurementId, ContextType... contextTypes) { |
| return getMeasurementContext(sourceName, entityService.find(sourceName, Measurement.class, measurementId), |
| contextTypes); |
| } |
| |
| /** |
| * Vavr conform version of contextActivity getTestStepContext function. |
| * |
| * returns the ordered and measurement context for a {@link Measurement}. If no |
| * {@link ContextType}s are defined for this method call, the method returns all |
| * context informations of the available {@link ContextType}s. Otherwise you can |
| * specify a list of {@link ContextType}s. |
| * |
| * Possible {@link ContextType}s are {@link ContextType}.UNITUNDERTEST, |
| * {@link ContextType}.TESTSEQUENCE and {@link ContextType}.TESTEQUIPMENT. |
| * |
| * @param sourceName name of the source (MDM {@link Environment} name) |
| * @param measurement {@link Try} of the {@link Measurement} |
| * @param contextTypes list of {@link ContextType}s |
| * @return the ordered and measured context data as context object for the |
| * identified {@link Measurement} |
| */ |
| private Try<Map<String, Map<ContextType, ContextRoot>>> getMeasurementContext(String sourceName, |
| Try<Measurement> measurement, ContextType... contextTypes) { |
| |
| Try<Map<ContextType, ContextRoot>> contextOrdered = entityService.getEntityManager(sourceName) |
| .map(e -> HashMap.ofAll(e.loadContexts(findTestStep(sourceName, measurement).get(), contextTypes))); |
| |
| Try<Map<ContextType, ContextRoot>> contextMeasured = entityService.getEntityManager(sourceName) |
| .map(e -> HashMap.ofAll(e.loadContexts(measurement.get(), contextTypes))); |
| |
| return Try.of(() -> Lazy.of(() -> HashMap.of(CONTEXT_GROUP_ORDERED, contextOrdered.get(), |
| CONTEXT_GROUP_MEASURED, contextMeasured.get())).get()); |
| } |
| |
| /** |
| * |
| * @param sourceName |
| * @return |
| */ |
| private Try<EntityManager> getEntityManager(Value<String> sourceName) { |
| return Try.of(() -> Lazy.of(() -> connectorService.getContextByName(sourceName.get()).getEntityManager() |
| .orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!"))).get()); |
| } |
| |
| /** |
| * |
| * @param sourceName |
| * @param testStep |
| * @return |
| */ |
| private Try<List<Measurement>> findMeasurements(String sourceName, Try<TestStep> testStep) { |
| return entityService.getEntityManager(sourceName).map( |
| e -> Lazy.of(() -> List.ofAll(e.loadChildren(testStep.get(), TestStep.CHILD_TYPE_MEASUREMENT))).get()); |
| } |
| |
| /** |
| * |
| * @param sourceName |
| * @param testStep |
| * @return |
| */ |
| private Try<TestStep> findTestStep(String sourceName, Try<Measurement> measurement) { |
| return entityService.getEntityManager(sourceName) |
| .map(e -> Lazy.of(() -> e.loadParent(measurement.get(), Measurement.PARENT_TYPE_TESTSTEP).get()).get()); |
| } |
| |
| /** |
| * Updates the context of the given ContextDescribable. |
| * |
| * @param body update data |
| * @param contextDescribable ContextDescribable to update |
| * @param contextTypes contextTypes which whould be updated. Empty means |
| * all contextTypes. |
| * @return |
| */ |
| public DescribableContexts updateContext(MDMEntityResponse body, ContextDescribable contextDescribable, |
| ContextType... contextTypes) { |
| Try<Map<String, Object>> contextMap = Try.of(() -> body.getData().get(0)).map(this::transformMap); |
| |
| DescribableContexts ec = updateContext(getTestStep(contextDescribable), contextMap, |
| getContextTypes(contextTypes)); |
| persist(ec); |
| return ec; |
| } |
| |
| private DescribableContexts updateContext(TestStep testStep, Try<Map<String, Object>> contextMap, |
| ContextType[] contextTypes) { |
| Map<ContextType, Object> orderedMap = contextMap.get().get(ContextActivity.CONTEXT_GROUP_ORDERED) |
| .map(this::transformMap).getOrElse(HashMap.empty()) |
| .mapKeys(t -> ServiceUtils.getContextTypeSupplier(t).get()) |
| .filterKeys(t -> Arrays.asList(contextTypes).contains(t)); |
| |
| DescribableContexts ec = new DescribableContexts(); |
| ec.setTestStep(testStep); |
| ec.setOrdered(updateContextDescribableContext(testStep, orderedMap, contextTypes)); |
| |
| Map<ContextType, Object> measuredMap = contextMap.get().get(ContextActivity.CONTEXT_GROUP_MEASURED) |
| .map(this::transformMap).getOrElse(HashMap.empty()) |
| .mapKeys(t -> ServiceUtils.getContextTypeSupplier(t).get()) |
| .filterKeys(t -> Arrays.asList(contextTypes).contains(t)); |
| |
| ec.setMeasurements(getEntityManager(testStep.getSourceName()).loadChildren(testStep, Measurement.class)); |
| ec.getAnyMeasurement().map(m -> updateContextDescribableContext(m, measuredMap, contextTypes)) |
| .ifPresent(ec::setMeasured); |
| |
| return ec; |
| } |
| |
| private java.util.Map<ContextType, ContextRoot> updateContextDescribableContext( |
| ContextDescribable contextDescribable, Map<ContextType, Object> rootMap, ContextType[] contextTypes) { |
| java.util.Map<ContextType, ContextRoot> existingRootMap = getEntityManager(contextDescribable.getSourceName()) |
| .loadContexts(contextDescribable, contextTypes); |
| |
| for (ContextType contextType : contextTypes) { |
| ContextRoot existingRoot = existingRootMap.computeIfAbsent(contextType, |
| ct -> createContextRoot(contextDescribable, ct)); |
| |
| rootMap.get(contextType).forEach(newContextRoot -> updateContextRoot(existingRoot, newContextRoot)); |
| } |
| return existingRootMap; |
| } |
| |
| private ContextRoot createContextRoot(ContextDescribable contextDescribable, ContextType contextType) { |
| EntityFactory factory = getEntityFactory(contextDescribable.getSourceName()); |
| |
| TemplateRoot templateRoot = findTemplateRoot(contextDescribable, contextType); |
| |
| if (contextDescribable instanceof Measurement) { |
| return factory.createContextRoot((Measurement) contextDescribable, templateRoot); |
| } else if (contextDescribable instanceof TestStep) { |
| return factory.createContextRoot((TestStep) contextDescribable, templateRoot); |
| } else { |
| throw new MDMEntityAccessException("Only entities TestStep and Measurement are supported!"); |
| } |
| } |
| |
| private void updateContextRoot(ContextRoot existingRoot, Object newContextRoot) { |
| transformList(newContextRoot).map(this::transformMap).map(this::transformToMDMEntity).forEach(mdmEntity -> { |
| ContextComponent comp = existingRoot.getContextComponent(mdmEntity.getName()) |
| .orElseGet(() -> getEntityFactory(existingRoot.getSourceName()) |
| .createContextComponent(mdmEntity.getName(), existingRoot)); |
| |
| updateContextComponent(comp, mdmEntity.getAttributes()); |
| }); |
| } |
| |
| private void updateContextComponent(ContextComponent contextComponent, java.util.List<MDMAttribute> nameValues) { |
| nameValues.forEach(attribute -> Serializer.applyValue(contextComponent.getValue(attribute.getName()), |
| attribute.getValue())); |
| } |
| |
| private TemplateRoot findTemplateRoot(ContextDescribable contextDescribable, ContextType contextType) { |
| TestStep testStep; |
| if (contextDescribable instanceof Measurement) { |
| testStep = getTestStep((Measurement) contextDescribable); |
| } else if (contextDescribable instanceof TestStep) { |
| testStep = (TestStep) contextDescribable; |
| } else { |
| throw new MDMEntityAccessException("Only entities TestStep and Measurement are supported!"); |
| } |
| TemplateTestStep tpl = TemplateTestStep.of(testStep).orElseThrow( |
| () -> new MDMEntityAccessException("Cannot find TemplateTestStep for TestStep: " + testStep)); |
| |
| return tpl.getTemplateRoot(contextType).orElseThrow(() -> new MDMEntityAccessException( |
| "Cannot find TemplateRoot for ContextType " + contextType + " on Template TestStep " + tpl)); |
| } |
| |
| private TestStep getTestStep(ContextDescribable contextDescribable) { |
| if (contextDescribable instanceof TestStep) { |
| return (TestStep) contextDescribable; |
| } else { |
| return getTestStep((Measurement) contextDescribable); |
| } |
| } |
| |
| private TestStep getTestStep(Measurement measurement) { |
| return getEntityManager(measurement.getSourceName()).loadParent(measurement, TestStep.class).orElseThrow( |
| () -> new MDMEntityAccessException("Cannot find parent TestStep of Measurement: " + measurement)); |
| } |
| |
| private EntityManager getEntityManager(String sourceName) { |
| return connectorService.getContextByName(sourceName).getEntityManager() |
| .orElseThrow(() -> new MDMEntityAccessException("Entity manager not present!")); |
| } |
| |
| private EntityFactory getEntityFactory(String sourceName) { |
| return connectorService.getContextByName(sourceName).getEntityFactory() |
| .orElseThrow(() -> new MDMEntityAccessException("Entity factory not present!")); |
| } |
| |
| private ContextType[] getContextTypes(ContextType[] types) { |
| if (types.length == 0) { |
| return ContextType.values(); |
| } else { |
| return types; |
| } |
| } |
| |
| private void persist(DescribableContexts ec) { |
| Transaction t = null; |
| try { |
| // start transaction to persist ValueList |
| t = getEntityManager(ec.getTestStep().getSourceName()).startTransaction(); |
| |
| java.util.Map<Boolean, java.util.List<Entity>> map = ec.getEntities().stream() |
| .collect(Collectors.groupingBy(this::isPersisted)); |
| |
| t.create(map.getOrDefault(Boolean.FALSE, Collections.emptyList())); |
| t.update(map.getOrDefault(Boolean.TRUE, Collections.emptyList())); |
| |
| // commit the transaction |
| t.commit(); |
| } catch (Exception e) { |
| if (t != null) { |
| t.abort(); |
| } |
| throw new MDMEntityAccessException(e.getMessage(), e); |
| } |
| } |
| |
| private boolean isPersisted(Entity e) { |
| return !Strings.isNullOrEmpty(e.getID()) && !"0".equals(e.getID()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private Map<String, Object> transformMap(Object obj) { |
| if (obj instanceof java.util.Map) { |
| return HashMap.ofAll(java.util.Map.class.cast(obj)); |
| } else { |
| throw new MDMEntityAccessException(String.format("Expected instance of '%s', but got '%s'", |
| java.util.Map.class.getName(), obj.getClass().getName())); |
| } |
| } |
| |
| /** |
| * Casts an {@link Object} holding a {@link java.util.List<Object>} to a |
| * {@link io.vavr.collection.List<Object>} |
| * |
| * @param obj the {@link Object} to cast |
| * @return the {@link io.vavr.collection.List<Object>} |
| */ |
| @SuppressWarnings("unchecked") |
| private List<Object> transformList(Object obj) { |
| if (obj instanceof java.util.List) { |
| return List.ofAll(java.util.List.class.cast(obj)); |
| } else { |
| throw new MDMEntityAccessException(String.format("Expected instance of '%s', but got '%s'", |
| java.util.List.class.getName(), obj.getClass().getName())); |
| } |
| } |
| |
| private MDMEntity transformToMDMEntity(Map<String, Object> component) { |
| return new MDMEntity( |
| component.get("name").map(Object::toString) |
| .getOrElseThrow(() -> new MDMEntityAccessException("Missing attribute 'name' in MDMEntity")), |
| component.get("id").map(Object::toString).getOrElse(""), |
| component.get("type").map(Object::toString).getOrElse(""), |
| component.get("sourceType").map(Object::toString).getOrElse(""), |
| component.get("sourceName").map(Object::toString).getOrElse(""), |
| transformList(component.get("attributes").getOrElseThrow( |
| () -> new MDMEntityAccessException("Missing attribute 'attributes' in MDMEntity"))) |
| .map(this::transformMap) |
| .map(m -> new MDMAttribute( |
| m.get("name").map(Object::toString) |
| .getOrElseThrow(() -> new MDMEntityAccessException( |
| "Missing attribute 'name' in MDMAttribute")), |
| m.get("value").map(Object::toString) |
| .getOrElseThrow(() -> new MDMEntityAccessException( |
| "Missing attribute 'value' in MDMAttribute")), |
| m.get("unit").map(Object::toString).getOrElse(""), |
| m.get("datatype").map(Object::toString).getOrElse(""))) |
| .toJavaList()); |
| } |
| } |