blob: bb24acfd0867d9a014d5e90a9c2a57be81f8b783 [file] [log] [blame]
/*
* Copyright (c) 2016 Gigatronik Ingolstadt GmbH and others
* 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 java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import org.asam.ods.AoException;
import org.asam.ods.InstanceElement;
import org.eclipse.mdm.api.base.ConnectionException;
import org.eclipse.mdm.api.base.FileService;
import org.eclipse.mdm.api.base.Transaction;
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.query.DataAccessException;
import org.eclipse.mdm.api.base.query.EntityType;
import org.eclipse.mdm.api.base.query.Filter;
import org.eclipse.mdm.api.base.query.Join;
import org.eclipse.mdm.api.base.query.ModelManager;
import org.eclipse.mdm.api.base.query.Query;
import org.eclipse.mdm.api.base.query.Record;
import org.eclipse.mdm.api.base.query.Result;
import org.eclipse.mdm.api.base.query.SearchService;
import org.eclipse.mdm.api.dflt.EntityManager;
import org.eclipse.mdm.api.dflt.model.EntityFactory;
import org.eclipse.mdm.api.odsadapter.filetransfer.CORBAFileService;
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.ODSModelManager;
import org.eclipse.mdm.api.odsadapter.search.ODSSearchService;
import org.eclipse.mdm.api.odsadapter.transaction.ODSTransaction;
import org.eclipse.mdm.api.odsadapter.utils.ODSConverter;
import org.eclipse.mdm.api.odsadapter.utils.ODSUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ASAM ODS implementation of the {@link EntityManager} interface.
*
* @since 1.0.0
* @author Viktor Stoehr, Gigatronik Ingolstadt GmbH
*/
public class ODSEntityManager implements EntityManager {
// ======================================================================
// Class variables
// ======================================================================
private static final Logger LOGGER = LoggerFactory.getLogger(ODSEntityManager.class);
// ======================================================================
// Instance variables
// ======================================================================
private final ODSModelManager modelManager;
private final EntityLoader entityLoader;
private final Transfer transfer = Transfer.SOCKET;
private String esHost;
// ======================================================================
// Constructors
// ======================================================================
/**
* Constructor.
*
* @param modelManager
* The {@link ODSModelManager}.
*/
public ODSEntityManager(ODSModelManager modelManager, String esHost) {
this.modelManager = modelManager;
this.esHost = esHost;
entityLoader = new EntityLoader(modelManager);
}
// ======================================================================
// Public methods
// ======================================================================
/**
* {@inheritDoc}
*/
@Override
public Optional<EntityFactory> getEntityFactory() {
try {
return Optional.of(new ODSEntityFactory(modelManager, loadLoggedOnUser().get()));
} catch (DataAccessException e) {
throw new IllegalStateException("Unable to load instance of the logged in user.");
}
}
/**
* {@inheritDoc}
*/
@Override
public Optional<ModelManager> getModelManager() {
return Optional.of(modelManager);
}
/**
* {@inheritDoc}
*/
@Override
public Optional<SearchService> getSearchService() {
// TODO
// java docs: cache this service for ONE request!
return Optional.of(new ODSSearchService(modelManager, entityLoader, esHost));
}
/**
* {@inheritDoc}
*/
@Override
public Optional<FileService> getFileService() {
if (modelManager.getFileServer() == null) {
return Optional.empty();
}
return Optional.of(new CORBAFileService(modelManager, transfer));
}
/**
* {@inheritDoc}
*/
@Override
public Environment loadEnvironment() throws DataAccessException {
List<Environment> environments = loadAll(Environment.class);
if (environments.size() != 1) {
throw new DataAccessException("Unable to laod the environment entity.");
}
return environments.get(0);
}
/**
* {@inheritDoc}
*/
@Override
public Optional<User> loadLoggedOnUser() throws DataAccessException {
InstanceElement ieUser = null;
try {
ieUser = modelManager.getAoSession().getUser();
return Optional.of(
entityLoader.load(new Key<>(User.class), Long.toString(ODSConverter.fromODSLong(ieUser.getId()))));
} catch (AoException e) {
throw new DataAccessException("Unable to load the logged in user entity due to: " + e.reason, e);
} finally {
try {
if (ieUser != null) {
ieUser.destroy();
ieUser._release();
}
} catch (AoException aoe) {
LOGGER.warn("Unable to destroy the CORBA resource due to: " + aoe.reason, aoe);
ieUser._release();
}
}
}
/**
* {@inheritDoc}
*/
@Override
public <T extends Entity> T load(Class<T> entityClass, String instanceID) throws DataAccessException {
return entityLoader.load(new Key<>(entityClass), instanceID);
}
/**
* {@inheritDoc}
*/
@Override
public <T extends Entity> T load(Class<T> entityClass, ContextType contextType, String instanceID)
throws DataAccessException {
return entityLoader.load(new Key<>(entityClass, contextType), instanceID);
}
/**
* {@inheritDoc}
*/
@Override
public <T extends Entity> Optional<T> loadParent(Entity child, Class<T> entityClass) throws DataAccessException {
EntityType parentEntityType = modelManager.getEntityType(entityClass);
EntityType childEntityType = modelManager.getEntityType(child);
Query query = modelManager.createQuery().selectID(parentEntityType);
if (child instanceof Channel && ChannelGroup.class.equals(entityClass)) {
// this covers the gap between channel and channel group via local
// column
EntityType localColumnEntityType = modelManager.getEntityType("LocalColumn");
query.join(childEntityType, localColumnEntityType).join(localColumnEntityType, parentEntityType);
} else {
query.join(childEntityType, parentEntityType);
}
Optional<String> instanceID = query.fetchSingleton(Filter.idOnly(childEntityType, child.getID()))
.map(r -> r.getRecord(parentEntityType)).map(Record::getID);
if (instanceID.isPresent()) {
return Optional.of(entityLoader.load(new Key<>(entityClass), instanceID.get()));
}
return Optional.empty();
}
/**
* {@inheritDoc}
*/
@Override
public <T extends Entity> List<T> loadAll(Class<T> entityClass, String pattern) throws DataAccessException {
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 Entity> List<T> loadAll(Class<T> entityClass, ContextType contextType, String pattern)
throws DataAccessException {
return entityLoader.loadAll(new Key<>(entityClass, contextType), pattern);
}
/**
* {@inheritDoc}
*/
@Override
public <T extends Entity> List<T> loadChildren(Entity parent, Class<T> entityClass, String pattern)
throws DataAccessException {
EntityType parentEntityType = modelManager.getEntityType(parent);
EntityType childEntityType = modelManager.getEntityType(entityClass);
Query query = modelManager.createQuery();
if (parent instanceof ChannelGroup && Channel.class.equals(entityClass)) {
// this covers the gap between channel and channel group via local
// column
EntityType localColumnEntityType = modelManager.getEntityType("LocalColumn");
query.join(childEntityType, localColumnEntityType).join(localColumnEntityType, parentEntityType);
} else {
query.join(childEntityType, parentEntityType);
}
List<String> instanceIDs = query.selectID(childEntityType)
.fetch(Filter.and().id(parentEntityType, parent.getID()).name(childEntityType, pattern)).stream()
.map(r -> r.getRecord(childEntityType)).map(Record::getID).collect(Collectors.toList());
return entityLoader.loadAll(new Key<>(entityClass), instanceIDs);
}
// /**
// * {@inheritDoc}
// */
// @Override
// public <T extends StatusAttachable> List<T> loadChildren(Entity parent,
// Class<T> entityClass, Status status,
// String pattern) throws DataAccessException {
// EntityType parentEntityType = modelManager.getEntityType(parent);
// EntityType childEntityType = modelManager.getEntityType(entityClass);
// EntityType statusEntityType =
// modelManager.getEntityType(status.getTypeName());
//
// List<String> instanceIDs = modelManager.createQuery()
// .join(childEntityType, parentEntityType, statusEntityType)
// .selectID(childEntityType)
// .fetch(Filter.and()
// .id(parentEntityType, parent.getID())
// .id(statusEntityType, status.getID())
// .name(childEntityType, pattern))
// .stream().map(r ->
// r.getRecord(childEntityType)).map(Record::getID).collect(Collectors.toList());
//
// return entityLoader.loadAll(new Key<>(entityClass), instanceIDs);
// }
/**
* {@inheritDoc}
*/
@Override
public List<ContextType> loadContextTypes(ContextDescribable contextDescribable) throws DataAccessException {
EntityType contextDescribableEntityType = modelManager.getEntityType(contextDescribable);
Query query = modelManager.createQuery();
Map<ContextType, EntityType> contextRootEntityTypes = new EnumMap<>(ContextType.class);
for (ContextType contextType : ContextType.values()) {
EntityType entityType = modelManager.getEntityType(ContextRoot.class, contextType);
contextRootEntityTypes.put(contextType, entityType);
query.join(contextDescribableEntityType.getRelation(entityType), Join.OUTER).selectID(entityType);
}
Optional<Result> result = query
.fetchSingleton(Filter.idOnly(contextDescribableEntityType, contextDescribable.getID()));
if (result.isPresent()) {
List<ContextType> contextTypes = new ArrayList<>();
for (Entry<ContextType, EntityType> entry : contextRootEntityTypes.entrySet()) {
Optional<String> instanceID = result.map(r -> r.getRecord(entry.getValue())).map(Record::getID);
if (instanceID.isPresent()) {
contextTypes.add(entry.getKey());
}
}
return contextTypes;
}
return Collections.emptyList();
}
/**
* {@inheritDoc}
*/
@Override
public Map<ContextType, ContextRoot> loadContexts(ContextDescribable contextDescribable,
ContextType... contextTypes) throws DataAccessException {
EntityType contextDescribableEntityType = modelManager.getEntityType(contextDescribable);
Query query = modelManager.createQuery();
Map<ContextType, EntityType> contextRootEntityTypes = new EnumMap<>(ContextType.class);
for (ContextType contextType : contextTypes.length == 0 ? ContextType.values() : contextTypes) {
EntityType entityType = modelManager.getEntityType(ContextRoot.class, contextType);
contextRootEntityTypes.put(contextType, entityType);
query.join(contextDescribableEntityType.getRelation(entityType), Join.OUTER).selectID(entityType);
}
Optional<Result> result = query
.fetchSingleton(Filter.idOnly(contextDescribableEntityType, contextDescribable.getID()));
if (result.isPresent()) {
Map<ContextType, ContextRoot> contextRoots = new EnumMap<>(ContextType.class);
for (Entry<ContextType, EntityType> entry : contextRootEntityTypes.entrySet()) {
String instanceID = result.get().getRecord(entry.getValue()).getID();
if (ODSUtils.isValidID(instanceID)) {
contextRoots.put(entry.getKey(),
entityLoader.load(new Key<>(ContextRoot.class, entry.getKey()), instanceID));
}
}
return contextRoots;
}
return Collections.emptyMap();
}
/**
* {@inheritDoc}
*/
@Override
public List<MeasuredValues> readMeasuredValues(ReadRequest readRequest) throws DataAccessException {
return new ReadRequestHandler(modelManager).execute(readRequest);
}
/**
* {@inheritDoc}
*/
@Override
public Transaction startTransaction() throws DataAccessException {
try {
return new ODSTransaction(modelManager, loadEnvironment(), transfer);
} catch (AoException e) {
throw new DataAccessException("Unable to start transaction due to: " + e.reason, e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void close() throws ConnectionException {
try {
modelManager.close();
} catch (AoException e) {
throw new ConnectionException("Unable to close the connection to the data source due to: " + e.reason, e);
}
}
}