| /******************************************************************************** |
| * Copyright (c) 2015-2019 Contributors to the Eclipse Foundation |
| * |
| * See the NOTICE file(s) distributed with this work for additional |
| * information regarding copyright ownership. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the 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; |
| |
| import static java.util.stream.Collectors.groupingBy; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Optional; |
| import java.util.concurrent.locks.Lock; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| import java.util.stream.Stream; |
| |
| import org.asam.ods.AIDName; |
| import org.asam.ods.AggrFunc; |
| import org.asam.ods.AoException; |
| import org.asam.ods.AoSession; |
| import org.asam.ods.ApplElem; |
| import org.asam.ods.ApplElemAccess; |
| import org.asam.ods.ApplRel; |
| import org.asam.ods.ApplicationStructureValue; |
| import org.asam.ods.ElemResultSetExt; |
| import org.asam.ods.EnumerationAttributeStructure; |
| import org.asam.ods.EnumerationItemStructure; |
| import org.asam.ods.EnumerationStructure; |
| import org.asam.ods.JoinDef; |
| import org.asam.ods.QueryStructureExt; |
| import org.asam.ods.SelAIDNameUnitId; |
| import org.asam.ods.SelItem; |
| import org.asam.ods.SelOrder; |
| import org.asam.ods.T_LONGLONG; |
| import org.eclipse.mdm.api.base.adapter.EntityType; |
| import org.eclipse.mdm.api.base.adapter.ModelManager; |
| import org.eclipse.mdm.api.base.adapter.Relation; |
| import org.eclipse.mdm.api.base.model.ContextType; |
| import org.eclipse.mdm.api.base.model.Entity; |
| import org.eclipse.mdm.api.base.model.EnumRegistry; |
| import org.eclipse.mdm.api.base.model.Enumeration; |
| import org.eclipse.mdm.api.base.model.Unit; |
| import org.eclipse.mdm.api.odsadapter.lookup.config.DefaultEntityConfigRepositoryLoader; |
| import org.eclipse.mdm.api.odsadapter.lookup.config.EntityConfig; |
| import org.eclipse.mdm.api.odsadapter.lookup.config.EntityConfig.Key; |
| import org.eclipse.mdm.api.odsadapter.lookup.config.EntityConfigRepository; |
| import org.eclipse.mdm.api.odsadapter.lookup.config.EntityConfigRepositoryLoader; |
| import org.eclipse.mdm.api.odsadapter.utils.ODSConverter; |
| import org.eclipse.mdm.api.odsadapter.utils.ODSEnum; |
| import org.eclipse.mdm.api.odsadapter.utils.ODSEnumerations; |
| import org.omg.CORBA.ORB; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * ODS implementation of the {@link ModelManager} interface. |
| * |
| * @since 1.0.0 |
| * @author Viktor Stoehr, Gigatronik Ingolstadt GmbH |
| */ |
| public class ODSModelManager implements ModelManager { |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(ODSModelManager.class); |
| |
| private final Map<String, EntityType> entityTypesByName = new HashMap<>(); |
| |
| private final ORB orb; |
| |
| private final Lock write; |
| private final Lock read; |
| |
| private EntityConfigRepository entityConfigRepository; |
| private EntityConfigRepositoryLoader entityConfigRepositoryLoader; |
| |
| private ApplElemAccess applElemAccess; |
| private AoSession aoSession; |
| |
| /** |
| * Constructor. |
| * |
| * @param orb Used to activate CORBA service objects. |
| * @param aoSession The underlying ODS session. |
| * @throws AoException Thrown on errors. |
| */ |
| public ODSModelManager(ORB orb, AoSession aoSession) throws AoException { |
| this(orb, aoSession, new DefaultEntityConfigRepositoryLoader()); |
| } |
| |
| /** |
| * Constructor. |
| * |
| * @param orb Used to activate CORBA service objects. |
| * @param aoSession The underlying ODS session. |
| * @throws AoException Thrown on errors. |
| */ |
| public ODSModelManager(ORB orb, AoSession aoSession, EntityConfigRepositoryLoader entityConfigRepositoryLoader) |
| throws AoException { |
| this.aoSession = aoSession; |
| this.orb = orb; |
| applElemAccess = aoSession.getApplElemAccess(); |
| this.entityConfigRepositoryLoader = entityConfigRepositoryLoader; |
| |
| // setup locks |
| ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); |
| write = reentrantReadWriteLock.writeLock(); |
| read = reentrantReadWriteLock.readLock(); |
| |
| initialize(); |
| } |
| |
| /** |
| * Returns the {@link ORB}. |
| * |
| * @return The {@code ORB} is returned. |
| */ |
| public ORB getORB() { |
| return orb; |
| } |
| |
| /** |
| * Returns the non root {@link EntityConfig} for given {@link Key}. |
| * |
| * @param <T> The concrete entity type. |
| * @param key Used as identifier. |
| * @return The non root {@code EntityConfig} is returned. |
| */ |
| public <T extends Entity> EntityConfig<T> findEntityConfig(Key<T> key) { |
| read.lock(); |
| |
| try { |
| return entityConfigRepository.find(key); |
| } finally { |
| read.unlock(); |
| } |
| } |
| |
| /** |
| * Returns the root {@link EntityConfig} for given {@link Key}. |
| * |
| * @param <T> The concrete entity type. |
| * @param key Used as identifier. |
| * @return The root {@code EntityConfig} is returned. |
| */ |
| public <T extends Entity> EntityConfig<T> getEntityConfig(Key<T> key) { |
| read.lock(); |
| |
| try { |
| return entityConfigRepository.findRoot(key); |
| } finally { |
| read.unlock(); |
| } |
| } |
| |
| /** |
| * Returns the {@link EntityConfig} associated with given {@link EntityType}. |
| * |
| * @param entityType Used as identifier. |
| * @return The {@code EntityConfig} is returned. |
| */ |
| public EntityConfig<?> getEntityConfig(EntityType entityType) { |
| read.lock(); |
| |
| try { |
| return entityConfigRepository.find(entityType); |
| } finally { |
| read.unlock(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public List<EntityType> listEntityTypes() { |
| read.lock(); |
| |
| try { |
| return Collections.unmodifiableList(new ArrayList<>(entityTypesByName.values())); |
| } finally { |
| read.unlock(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public EntityType getEntityType(Class<? extends Entity> entityClass) { |
| read.lock(); |
| |
| try { |
| return getEntityConfig(new Key<>(entityClass)).getEntityType(); |
| } finally { |
| read.unlock(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public EntityType getEntityType(Class<? extends Entity> entityClass, ContextType contextType) { |
| read.lock(); |
| |
| try { |
| return getEntityConfig(new Key<>(entityClass, contextType)).getEntityType(); |
| } finally { |
| read.unlock(); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public EntityType getEntityType(String name) { |
| read.lock(); |
| |
| try { |
| EntityType entityType = entityTypesByName.get(name); |
| if (entityType == null) { |
| throw new IllegalArgumentException("Entity with name '" + name + "' not found."); |
| } |
| |
| return entityType; |
| } finally { |
| read.unlock(); |
| } |
| } |
| |
| @Override |
| public EntityType getEntityTypeById(String id) { |
| Optional<EntityType> entityType = listEntityTypes().stream().filter(et -> et.getId().equals(id)).findFirst(); |
| if (!entityType.isPresent()) { |
| throw new IllegalArgumentException("Entity with id '" + id + "' not found."); |
| } |
| |
| return entityType.get(); |
| } |
| |
| /** |
| * Returns the {@link AoSession} of this model manager. |
| * |
| * @return The {@code AoSession} is returned. |
| */ |
| public AoSession getAoSession() { |
| write.lock(); |
| |
| try { |
| return aoSession; |
| } finally { |
| write.unlock(); |
| } |
| } |
| |
| /** |
| * Returns the {@link ApplElemAccess} of this model manager. |
| * |
| * @return The {@code ApplElemAccess} is returned. |
| */ |
| public ApplElemAccess getApplElemAccess() { |
| read.lock(); |
| |
| try { |
| return applElemAccess; |
| } finally { |
| read.unlock(); |
| } |
| } |
| |
| /** |
| * Closes the ODS connection. |
| * |
| * @throws AoException Thrown on errors. |
| */ |
| public void close() throws AoException { |
| read.lock(); |
| |
| try { |
| applElemAccess._release(); |
| aoSession.close(); |
| } finally { |
| read.unlock(); |
| aoSession._release(); |
| } |
| } |
| |
| /** |
| * Reloads the complete session context. |
| */ |
| public void reloadApplicationModel() { |
| write.lock(); |
| |
| AoSession aoSessionOld = aoSession; |
| ApplElemAccess applElemAccessOld = applElemAccess; |
| try { |
| entityTypesByName.clear(); |
| |
| aoSession = aoSession.createCoSession(); |
| applElemAccess = aoSession.getApplElemAccess(); |
| initialize(); |
| } catch (AoException e) { |
| LOGGER.error("Unable to reload the application model due to: " + e.reason, e); |
| } finally { |
| write.unlock(); |
| } |
| |
| try { |
| applElemAccessOld._release(); |
| aoSessionOld.close(); |
| } catch (AoException e) { |
| LOGGER.debug("Unable to close replaced session due to: " + e.reason, e); |
| } finally { |
| aoSessionOld._release(); |
| } |
| } |
| |
| /** |
| * Initializes this model manager by caching the application model and loading |
| * the {@link EntityConfig}s. |
| * |
| * @throws AoException Thrown on errors. |
| */ |
| private void initialize() throws AoException { |
| loadApplicationModel(); |
| entityConfigRepository = entityConfigRepositoryLoader.loadEntityConfigurations(this); |
| } |
| |
| /** |
| * Caches the whole application model as provided by the ODS session. |
| * |
| * @throws AoException Thrown on errors. |
| */ |
| private void loadApplicationModel() throws AoException { |
| LOGGER.debug("Reading the application model..."); |
| |
| Map<String, Map<String, Enumeration<?>>> enumClassMap = loadODSEnumerations(); |
| |
| long start = System.currentTimeMillis(); |
| ApplicationStructureValue applicationStructureValue = aoSession.getApplicationStructureValue(); |
| Map<String, String> units = getUnitMapping(applicationStructureValue.applElems); |
| |
| // create entity types (incl. attributes) |
| Map<String, ODSEntityType> entityTypesByID = new HashMap<>(); |
| String sourceName = aoSession.getName(); |
| for (ApplElem applElem : applicationStructureValue.applElems) { |
| String odsID = Long.toString(ODSConverter.fromODSLong(applElem.aid)); |
| Map<String, Enumeration<?>> entityEnumMap = enumClassMap.getOrDefault(odsID, new HashMap<>()); |
| |
| ODSEntityType entityType = new ODSEntityType(sourceName, applElem, units, entityEnumMap); |
| entityTypesByName.put(applElem.aeName, entityType); |
| entityTypesByID.put(odsID, entityType); |
| } |
| |
| // create relations |
| List<Relation> relations = new ArrayList<>(); |
| for (ApplRel applRel : applicationStructureValue.applRels) { |
| EntityType source = entityTypesByID.get(Long.toString(ODSConverter.fromODSLong(applRel.elem1))); |
| EntityType target = entityTypesByID.get(Long.toString(ODSConverter.fromODSLong(applRel.elem2))); |
| relations.add(new ODSRelation(applRel, source, target)); |
| } |
| |
| // assign relations to their source entity types |
| relations.stream().collect(groupingBy(Relation::getSource)) |
| .forEach((e, r) -> ((ODSEntityType) e).setRelations(r)); |
| |
| long stop = System.currentTimeMillis(); |
| LOGGER.debug("{} entity types with {} relations found in {} ms.", entityTypesByName.size(), relations.size(), |
| stop - start); |
| } |
| |
| private Map<String, Map<String, Enumeration<?>>> loadODSEnumerations() throws AoException { |
| LOGGER.debug("Loading ODS enumerations..."); |
| long t1 = System.currentTimeMillis(); |
| |
| // enumeration mappings (aeID -> (aaName -> enumClass)) |
| Map<String, Map<String, Enumeration<?>>> enumClassMap = new HashMap<>(); |
| EnumRegistry er = EnumRegistry.getInstance(); |
| |
| Map<String, EnumerationItemStructure[]> map = new HashMap<>(); |
| for (EnumerationStructure es : aoSession.getEnumerationStructure()) { |
| map.put(es.enumName, es.items); |
| } |
| |
| for (EnumerationAttributeStructure eas : aoSession.getEnumerationAttributes()) { |
| |
| // make sure the enumeration is found |
| if (ODSEnumerations.getEnumObj(eas.enumName) == null) { |
| Enumeration<ODSEnum> enumdyn = new Enumeration<>(ODSEnum.class, eas.enumName); |
| for (EnumerationItemStructure enumItemStruct : map.get(eas.enumName)) { |
| LOGGER.trace("{}:{}:{}", eas.enumName, enumItemStruct.itemName, enumItemStruct.index); |
| enumdyn.addValue(new ODSEnum(enumItemStruct.itemName, enumItemStruct.index)); |
| } |
| er.add(eas.enumName, enumdyn); |
| } |
| |
| enumClassMap.computeIfAbsent(Long.toString(ODSConverter.fromODSLong(eas.aid)), k -> new HashMap<>()) |
| .put(eas.aaName, ODSEnumerations.getEnumObj(eas.enumName)); |
| } |
| LOGGER.debug("Loading enumerations took {}ms", System.currentTimeMillis() - t1); |
| return enumClassMap; |
| } |
| |
| /** |
| * Loads all available {@link Unit} names mapped by their instance IDs. |
| * |
| * @param applElems The application element meta data instances. |
| * @return The unit names mapped by the corresponding instance IDs. |
| * @throws AoException Thrown if unable to load the unit mappings. |
| */ |
| private Map<String, String> getUnitMapping(ApplElem[] applElems) throws AoException { |
| ApplElem unitElem = Stream.of(applElems).filter(ae -> ae.beName.equals("AoUnit")).findAny() |
| .orElseThrow(() -> new IllegalStateException("Application element 'Unit' is not defined.")); |
| |
| QueryStructureExt qse = new QueryStructureExt(); |
| qse.anuSeq = new SelAIDNameUnitId[] { |
| new SelAIDNameUnitId(new AIDName(unitElem.aid, "Id"), new T_LONGLONG(), AggrFunc.NONE), |
| new SelAIDNameUnitId(new AIDName(unitElem.aid, "Name"), new T_LONGLONG(), AggrFunc.NONE) }; |
| qse.condSeq = new SelItem[0]; |
| qse.groupBy = new AIDName[0]; |
| qse.joinSeq = new JoinDef[0]; |
| qse.orderBy = new SelOrder[0]; |
| |
| Map<String, String> units = new HashMap<>(); |
| ElemResultSetExt unitResultSetExt = getApplElemAccess().getInstancesExt(qse, 0)[0].firstElems[0]; |
| for (int i = 0; i < unitResultSetExt.values[0].value.flag.length; i++) { |
| String unitID = Long |
| .toString(ODSConverter.fromODSLong(unitResultSetExt.values[0].value.u.longlongVal()[i])); |
| String unitName = unitResultSetExt.values[1].value.u.stringVal()[i]; |
| units.put(unitID, unitName); |
| } |
| |
| return units; |
| } |
| } |