/** * Licensed to the Austrian Association for Software Tool Integration (AASTI) * under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. The AASTI licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.openengsb.core.ekb.persistence.query.edb.internal; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.openengsb.core.api.model.CommitMetaInfo; import org.openengsb.core.api.model.CommitQueryRequest; import org.openengsb.core.api.model.ModelDescription; import org.openengsb.core.api.model.QueryRequest; import org.openengsb.core.edb.api.EDBCommit; import org.openengsb.core.edb.api.EDBConstants; import org.openengsb.core.edb.api.EDBException; import org.openengsb.core.edb.api.EDBObject; import org.openengsb.core.edb.api.EngineeringDatabaseService; import org.openengsb.core.ekb.api.EKBCommit; import org.openengsb.core.ekb.api.EKBException; import org.openengsb.core.ekb.api.ModelRegistry; import org.openengsb.core.ekb.api.QueryInterface; import org.openengsb.core.ekb.api.QueryParser; import org.openengsb.core.ekb.common.EDBConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Implementation of the QueryInterface service. It's main responsibilities are the loading of elements from the EDB and * converting them to the correct format. */ public class QueryInterfaceService implements QueryInterface { private static final Logger LOGGER = LoggerFactory.getLogger(QueryInterfaceService.class); private EngineeringDatabaseService edbService; private EDBConverter edbConverter; private ModelRegistry modelRegistry; private List<QueryParser> queryParsers; @Override public <T> T getModel(Class<T> model, String oid) { LOGGER.debug("Invoked getModel with the model {} and the oid {}", model.getName(), oid); EDBObject object = edbService.getObject(oid); return edbConverter.convertEDBObjectToModel(model, object); } @Override public <T> List<T> getModelHistory(Class<T> model, String oid) { LOGGER.debug("Invoked getModelHistory with the model {} and the oid {}", model.getName(), oid); return edbConverter.convertEDBObjectsToModelObjects(model, edbService.getHistory(oid)); } @Override public <T> List<T> getModelHistoryForTimeRange(Class<T> model, String oid, Long from, Long to) { LOGGER.debug("Invoked getModelHistoryForTimeRange with the model {} and the oid {} for the " + "time period of {} to {}", new Object[]{ model.getName(), oid, new Date(from).toString(), new Date(to).toString() }); return edbConverter.convertEDBObjectsToModelObjects(model, edbService.getHistoryForTimeRange(oid, from, to)); } @Override public <T> List<T> query(Class<T> model, QueryRequest request) { LOGGER.debug("Query for model {} with the request {}", model.getName(), request); request.setModelClassName(model.getName()); return edbConverter.convertEDBObjectsToModelObjects(model, edbService.query(request)); } @Override public <T> List<T> queryByString(Class<T> model, String query) { return query(model, parseQueryString(query)); } @Override public <T> List<T> queryByStringAndTimestamp(Class<T> model, String query, String timestamp) { Long time; if (timestamp == null || timestamp.isEmpty()) { LOGGER.debug("Got invalid timestamp string. Use the current timestamp instead"); time = System.currentTimeMillis(); } else { time = Long.parseLong(timestamp); } QueryRequest request = parseQueryString(query); request.setTimestamp(time); return query(model, request); } @Override public QueryRequest parseQueryString(String query) throws EKBException { if (query.isEmpty()) { return QueryRequest.create(); } for (QueryParser parser : queryParsers) { if (parser.isParsingPossible(query)) { return parser.parseQueryString(query); } } throw new EKBException("There is no active parser which is able to parse the query string " + query); } @Override public <T> List<T> queryForActiveModels(Class<T> model) { LOGGER.debug("Invoked queryForActiveModels with the model {}", model.getName()); return query(model, QueryRequest.create()); } @Override public UUID getCurrentRevisionNumber() { return edbService.getCurrentRevisionNumber(); } @Override public UUID getLastRevisionNumberOfContext(String contextId) { return edbService.getLastRevisionNumberOfContext(contextId); } @Override public List<CommitMetaInfo> queryForCommits(CommitQueryRequest request) throws EKBException { return edbService.getRevisionsOfMatchingCommits(request); } @Override public EKBCommit loadCommit(String revision) throws EKBException { try { EDBCommit commit = edbService.getCommitByRevision(revision); return convertEDBCommitToEKBCommit(commit); } catch (EDBException e) { throw new EKBException("There is no commit with the revision " + revision); } } /** * Converts an EDBCommit object into an EKBCommit object. */ private EKBCommit convertEDBCommitToEKBCommit(EDBCommit commit) throws EKBException { EKBCommit result = new EKBCommit(); Map<ModelDescription, Class<?>> cache = new HashMap<>(); result.setRevisionNumber(commit.getRevisionNumber()); result.setComment(commit.getComment()); result.setParentRevisionNumber(commit.getParentRevisionNumber()); result.setDomainId(commit.getDomainId()); result.setConnectorId(commit.getConnectorId()); result.setInstanceId(commit.getInstanceId()); for (EDBObject insert : commit.getInserts()) { result.addInsert(createModelOfEDBObject(insert, cache)); } for (EDBObject update : commit.getUpdates()) { result.addUpdate(createModelOfEDBObject(update, cache)); } for (String delete : commit.getDeletions()) { EDBObject object = edbService.getObject(delete, commit.getTimestamp()); result.addDelete(createModelOfEDBObject(object, cache)); } return result; } /** * Converts an EDBObject instance into a model. For this, the method need to retrieve the model class to be able to * instantiate the corresponding model objects. If the conversion fails, null is returned. */ private Object createModelOfEDBObject(EDBObject object, Map<ModelDescription, Class<?>> cache) { try { ModelDescription description = getDescriptionFromObject(object); Class<?> modelClass; if (cache.containsKey(description)) { modelClass = cache.get(description); } else { modelClass = modelRegistry.loadModel(description); cache.put(description, modelClass); } return edbConverter.convertEDBObjectToModel(modelClass, object); } catch (IllegalArgumentException | ClassNotFoundException e) { LOGGER.warn("Unable to create model of the object {}", object.getOID(), e); return null; } } /** * Extracts the required values to lookup a model class from the given EDBObject. If this object does not contain * the required information, an IllegalArgumentException is thrown. */ private ModelDescription getDescriptionFromObject(EDBObject obj) { String modelName = obj.getString(EDBConstants.MODEL_TYPE); String modelVersion = obj.getString(EDBConstants.MODEL_TYPE_VERSION); if (modelName == null || modelVersion == null) { throw new IllegalArgumentException("The object " + obj.getOID() + " contains no model information"); } return new ModelDescription(modelName, modelVersion); } public void setEdbService(EngineeringDatabaseService edbService) { this.edbService = edbService; } public void setEdbConverter(EDBConverter edbConverter) { this.edbConverter = edbConverter; } public void setModelRegistry(ModelRegistry modelRegistry) { this.modelRegistry = modelRegistry; } public void setQueryParsers(List<QueryParser> queryParsers) { this.queryParsers = queryParsers; } }