/*******************************************************************************
* Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2019)
*
* contact.vitam@culture.gouv.fr
*
* This software is a computer program whose purpose is to implement a digital archiving back-office system managing
* high volumetry securely and efficiently.
*
* This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free
* software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as
* circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
* users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the
* successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or
* developing or reproducing the software by the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it is reserved for developers and
* experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling the security of their systems and/or data
* to be ensured and, more generally, to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you
* accept its terms.
*******************************************************************************/
package fr.gouv.vitam.logbook.common.server.database.collections;
import static com.mongodb.client.model.Filters.and;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Filters.or;
import static com.mongodb.client.model.Indexes.hashed;
import static fr.gouv.vitam.common.database.builder.query.QueryHelper.or;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.conversions.Bson;
import org.bson.json.JsonMode;
import org.bson.json.JsonWriterSettings;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortBuilder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.ErrorCategory;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoException;
import com.mongodb.client.FindIterable;
import com.mongodb.client.ListIndexesIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoIterable;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import fr.gouv.vitam.common.ParametersChecker;
import fr.gouv.vitam.common.database.builder.query.BooleanQuery;
import fr.gouv.vitam.common.database.builder.query.QueryHelper;
import fr.gouv.vitam.common.database.builder.request.exception.InvalidCreateOperationException;
import fr.gouv.vitam.common.database.builder.request.single.Select;
import fr.gouv.vitam.common.database.parser.request.single.SelectParserSingle;
import fr.gouv.vitam.common.database.parser.request.single.SelectToMongoDb;
import fr.gouv.vitam.common.database.server.mongodb.EmptyMongoCursor;
import fr.gouv.vitam.common.database.server.mongodb.MongoDbAccess;
import fr.gouv.vitam.common.database.server.mongodb.VitamDocument;
import fr.gouv.vitam.common.database.translators.elasticsearch.QueryToElasticsearch;
import fr.gouv.vitam.common.database.translators.mongodb.QueryToMongodb;
import fr.gouv.vitam.common.database.translators.mongodb.SelectToMongodb;
import fr.gouv.vitam.common.database.translators.mongodb.VitamDocumentCodec;
import fr.gouv.vitam.common.exception.DatabaseException;
import fr.gouv.vitam.common.exception.InvalidParseOperationException;
import fr.gouv.vitam.common.json.JsonHandler;
import fr.gouv.vitam.common.logging.VitamLogger;
import fr.gouv.vitam.common.logging.VitamLoggerFactory;
import fr.gouv.vitam.common.parameter.ParameterHelper;
import fr.gouv.vitam.common.thread.VitamThreadUtils;
import fr.gouv.vitam.logbook.common.parameters.LogbookLifeCycleObjectGroupParameters;
import fr.gouv.vitam.logbook.common.parameters.LogbookLifeCycleUnitParameters;
import fr.gouv.vitam.logbook.common.parameters.LogbookOperationParameters;
import fr.gouv.vitam.logbook.common.parameters.LogbookParameterName;
import fr.gouv.vitam.logbook.common.parameters.LogbookParameters;
import fr.gouv.vitam.logbook.common.parameters.LogbookTypeProcess;
import fr.gouv.vitam.logbook.common.server.LogbookDbAccess;
import fr.gouv.vitam.logbook.common.server.database.collections.request.LogbookVarNameAdapter;
import fr.gouv.vitam.logbook.common.server.exception.LogbookAlreadyExistsException;
import fr.gouv.vitam.logbook.common.server.exception.LogbookDatabaseException;
import fr.gouv.vitam.logbook.common.server.exception.LogbookException;
import fr.gouv.vitam.logbook.common.server.exception.LogbookExecutionException;
import fr.gouv.vitam.logbook.common.server.exception.LogbookNotFoundException;
/**
* MongoDb Access implementation base class
*
*/
public final class LogbookMongoDbAccessImpl extends MongoDbAccess implements LogbookDbAccess {
private static final String ITEM_CANNOT_BE_NULL = "Item cannot be null";
private static final String AT_LEAST_ONE_ITEM_IS_NEEDED = "At least one item is needed";
private static final String LOGBOOK_LIFE_CYCLE_NOT_FOUND = "LogbookLifeCycle not found";
private static final String SELECT_ISSUE = "Select issue";
private static final String ELEMENT_ALREADY_EXISTS = " (element already exists)";
private static final String TIMEOUT_OPERATION = " (timeout operation)";
private static final String EXISTS_ISSUE = "Exists issue";
/**
* SLICE command to optimize listing
*/
private static final String SLICE = "$slice";
private static final String UPDATE_NOT_FOUND_ITEM = "Update not found item: ";
private static final VitamLogger LOGGER =
VitamLoggerFactory.getInstance(LogbookMongoDbAccessImpl.class);
private static final String SELECT_PARAMETER_IS_NULL = "select parameter is null";
private static final String LIFECYCLE_ITEM = "lifecycleItem";
private static final String OPERATION_ITEM = "operationItem";
private static final String CREATION_ISSUE = "Creation issue";
private static final String UPDATE_ISSUE = "Update issue";
private static final String ROLLBACK_ISSUE = "Rollback issue";
private static final String INIT_UPDATE_LIFECYCLE = "Initialize update lifeCycle process";
private static final String ANOTHER_UPDATE_OPERATION_INPROCESS = "An update operation already in process";
/**
* Quick projection for ID Only
*/
static final BasicDBObject ID_PROJECTION = new BasicDBObject(LogbookDocument.ID, 1);
static final ObjectNode DEFAULT_SLICE = JsonHandler.createObjectNode();
static final ObjectNode DEFAULT_SLICE_WITH_ALL_EVENTS = JsonHandler.createObjectNode().put("events", 1);
static final ObjectNode DEFAULT_ALLKEYS = JsonHandler.createObjectNode();
static final int LAST_EVENT_SLICE = -1;
static final int TWO_LAST_EVENTS_SLICE = -2;
private static final String OB_ID = "obId";
static {
DEFAULT_SLICE.putObject(LogbookDocument.EVENTS).put(SLICE, LAST_EVENT_SLICE);
for (final LogbookMongoDbName name : LogbookMongoDbName.values()) {
DEFAULT_ALLKEYS.put(name.getDbname(), 1);
}
}
private final LogbookElasticsearchAccess esClient;
/**
* Constructor
*
* @param mongoClient MongoClient
* @param dbname MongoDB database name
* @param recreate True to recreate the index
* @param esClient elastic search client
* @param tenants the tenants list
* @throws IllegalArgumentException if mongoClient or dbname is null
*/
public LogbookMongoDbAccessImpl(MongoClient mongoClient, final String dbname, final boolean recreate,
LogbookElasticsearchAccess esClient, List<Integer> tenants) {
super(mongoClient, dbname, recreate);
this.esClient = esClient;
LogbookCollections.OPERATION.initialize(getMongoDatabase(), recreate);
LogbookCollections.LIFECYCLE_UNIT.initialize(getMongoDatabase(), recreate);
LogbookCollections.LIFECYCLE_OBJECTGROUP.initialize(getMongoDatabase(), recreate);
LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS.initialize(getMongoDatabase(), recreate);
LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS.initialize(getMongoDatabase(), recreate);
// init Logbook Operation Mapping for ES
LogbookCollections.OPERATION.initialize(this.esClient);
for (Integer tenant : tenants) {
LogbookCollections.OPERATION.getEsClient().addIndex(LogbookCollections.OPERATION, tenant);
}
}
/**
*
* @return The MongoCLientOptions to apply to MongoClient
*/
static final MongoClientOptions getMongoClientOptions() {
final VitamDocumentCodec<LogbookOperation> operationCodec = new VitamDocumentCodec<>(LogbookOperation.class);
final VitamDocumentCodec<LogbookLifeCycleUnit> lifecycleUnitCodec =
new VitamDocumentCodec<>(LogbookLifeCycleUnit.class);
final VitamDocumentCodec<LogbookLifeCycleObjectGroup> lifecycleObjectGroupCodec =
new VitamDocumentCodec<>(LogbookLifeCycleObjectGroup.class);
final VitamDocumentCodec<LogbookLifeCycleUnitInProcess> lifecycleUnitInProcessCodec =
new VitamDocumentCodec<>(LogbookLifeCycleUnitInProcess.class);
final VitamDocumentCodec<LogbookLifeCycleObjectGroupInProcess> lifecycleObjectGroupInProcessCodec =
new VitamDocumentCodec<>(LogbookLifeCycleObjectGroupInProcess.class);
final CodecRegistry codecRegistry = CodecRegistries.fromRegistries(MongoClient.getDefaultCodecRegistry(),
CodecRegistries.fromCodecs(operationCodec, lifecycleUnitCodec, lifecycleObjectGroupCodec,
lifecycleUnitInProcessCodec, lifecycleObjectGroupInProcessCodec));
return MongoClientOptions.builder().codecRegistry(codecRegistry).build();
}
/**
* Close database access
*/
@Override
public final void close() {
getMongoClient().close();
}
/**
* Ensure that all MongoDB database schema are indexed
*/
static final void ensureIndex() {
for (final LogbookCollections col : LogbookCollections.values()) {
if (col.getCollection() != null) {
col.getCollection().createIndex(hashed(LogbookDocument.ID));
}
}
LogbookOperation.addIndexes();
LogbookLifeCycle.addIndexes();
}
/**
* Remove temporarily the MongoDB Index (import optimization?)
*/
static final void removeIndexBeforeImport() {
LogbookOperation.dropIndexes();
LogbookLifeCycle.dropIndexes();
}
/**
* Reset MongoDB Index (import optimization?)
*/
static final void resetIndexAfterImport() {
LOGGER.info("Rebuild indexes");
ensureIndex();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
// get a list of the collections in this database and print them out
final MongoIterable<String> collectionNames = getMongoDatabase().listCollectionNames();
for (final String s : collectionNames) {
builder.append(s).append('\n');
}
for (final LogbookCollections coll : LogbookCollections.values()) {
if (coll != null && coll.getCollection() != null) {
final MongoCollection<?> mcoll = coll.getCollection();
builder.append(coll.getName()).append(" [").append(mcoll.count()).append('\n');
final ListIndexesIterable<Document> list = mcoll.listIndexes();
for (final Document dbObject : list) {
builder.append("\t").append(mcoll.count()).append(' ').append(dbObject).append('\n');
}
}
}
return builder.toString();
}
/**
* @return the Elasticsearch Acess Logbook client
*/
public LogbookElasticsearchAccess getEsClient() {
return esClient;
}
@Override
public final long getLogbookOperationSize() {
return LogbookCollections.OPERATION.getCollection().count();
}
@Override
public final long getLogbookLifeCyleUnitSize() {
return LogbookCollections.LIFECYCLE_UNIT.getCollection().count();
}
@Override
public final long getLogbookLifeCyleObjectGroupSize() throws LogbookDatabaseException, LogbookNotFoundException {
return LogbookCollections.LIFECYCLE_OBJECTGROUP.getCollection().count();
}
@Override
public long getLogbookLifeCyleUnitInProcessSize() throws LogbookDatabaseException, LogbookNotFoundException {
return LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS.getCollection().count();
}
@Override
public long getLogbookLifeCyleObjectGroupInProcessSize() throws LogbookDatabaseException, LogbookNotFoundException {
return LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS.getCollection().count();
}
/**
* Force flush on disk (MongoDB): should not be used
*/
final void flushOnDisk() {
getMongoAdmin().runCommand(new BasicDBObject("fsync", 1).append("async", true)
.append("lock", false));
}
/**
* Example code to get the reason from MongoException. <br>
* <br>
* Results are: DUPLICATE_KEY (duplicate entry), EXECUTION_TIMEOUT, UNCATEGORIZED
*
* @param e
* @return the ErrorCategory
*/
private ErrorCategory getErrorCategory(MongoException e) {
return ErrorCategory.fromErrorCode(e.getCode());
}
@SuppressWarnings("unchecked")
@Override
public MongoCursor<LogbookOperation> getLogbookOperations(JsonNode select, boolean sliced)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(SELECT_PARAMETER_IS_NULL, select);
// TODO P1 Temporary fix as the obIdIn (MessageIdentifier in the SEDA manifest) is only available on the 2 to
// last
// Logbook operation event . Must be removed when the processing will be reworked
if (sliced) {
final ObjectNode operationSlice = JsonHandler.createObjectNode();
operationSlice.putObject(LogbookDocument.EVENTS).put(SLICE, TWO_LAST_EVENTS_SLICE);
return select(LogbookCollections.OPERATION, select, operationSlice);
} else {
return select(LogbookCollections.OPERATION, select, DEFAULT_SLICE_WITH_ALL_EVENTS);
}
}
@SuppressWarnings("unchecked")
@Override
public MongoCursor<LogbookLifeCycle> getLogbookLifeCycleUnits(JsonNode select, boolean sliced,
LogbookCollections collection)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(SELECT_PARAMETER_IS_NULL, select);
if (sliced) {
final ObjectNode operationSlice = JsonHandler.createObjectNode();
operationSlice.putObject(LogbookDocument.EVENTS).put(SLICE, LAST_EVENT_SLICE);
return select(collection, select, operationSlice);
} else {
return select(collection, select, DEFAULT_SLICE_WITH_ALL_EVENTS);
}
}
@SuppressWarnings("unchecked")
@Override
public MongoCursor<LogbookLifeCycleUnit> getLogbookLifeCycleUnitsFull(LogbookCollections collection, Select select)
throws LogbookDatabaseException {
ParametersChecker.checkParameter(SELECT_PARAMETER_IS_NULL, select);
try {
return selectExecute(collection, select);
} catch (final InvalidParseOperationException e) {
throw new LogbookDatabaseException(e);
}
}
@SuppressWarnings("unchecked")
@Override
public MongoCursor<LogbookLifeCycle> getLogbookLifeCycleObjectGroups(JsonNode select, boolean sliced,
LogbookCollections collection)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(SELECT_PARAMETER_IS_NULL, select);
return select(collection, select, sliced);
}
@SuppressWarnings("unchecked")
@Override
public MongoCursor<LogbookLifeCycleObjectGroup> getLogbookLifeCycleObjectGroupsFull(LogbookCollections collection,
Select select)
throws LogbookDatabaseException {
ParametersChecker.checkParameter(SELECT_PARAMETER_IS_NULL, select);
try {
return selectExecute(collection, select);
} catch (final InvalidParseOperationException e) {
throw new LogbookDatabaseException(e);
}
}
/**
* Check if one id exists already
*
* @param collection
* @param id
* @return True if one LogbookDocument<?> object exists with this id
* @throws LogbookDatabaseException
*/
final boolean exists(final LogbookCollections collection, final String id)
throws LogbookDatabaseException {
try {
return collection.getCollection().find(eq(LogbookDocument.ID,
id)).projection(ID_PROJECTION).first() != null;
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(EXISTS_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
EXISTS_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
}
}
@Override
public final boolean existsLogbookOperation(final String operationItem)
throws LogbookDatabaseException {
ParametersChecker.checkParameter(OPERATION_ITEM, operationItem);
return exists(LogbookCollections.OPERATION, operationItem);
}
@Override
public boolean existsLogbookLifeCycleUnit(String unitId)
throws LogbookDatabaseException {
ParametersChecker.checkParameter(LIFECYCLE_ITEM, unitId);
return exists(LogbookCollections.LIFECYCLE_UNIT, unitId);
}
@Override
public boolean existsLogbookLifeCycleObjectGroup(String objectGroupId)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(LIFECYCLE_ITEM, objectGroupId);
return exists(LogbookCollections.LIFECYCLE_OBJECTGROUP, objectGroupId);
}
@SuppressWarnings("rawtypes")
final VitamDocument getLogbook(final LogbookCollections collection, final String id)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter("Logbook item", id);
VitamDocument item = null;
try {
item = (VitamDocument) collection.getCollection().find(eq(LogbookDocument.ID, id)).first();
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(SELECT_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
SELECT_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
}
if (item == null) {
throw new LogbookNotFoundException("Logbook item not found");
}
return item;
}
@Override
public LogbookOperation getLogbookOperation(String eventIdentifierProcess)
throws LogbookDatabaseException, LogbookNotFoundException {
return (LogbookOperation) getLogbook(LogbookCollections.OPERATION, eventIdentifierProcess);
}
@Override
public LogbookLifeCycleUnit getLogbookLifeCycleUnit(String unitId)
throws LogbookDatabaseException, LogbookNotFoundException {
return (LogbookLifeCycleUnit) getLogbook(LogbookCollections.LIFECYCLE_UNIT, unitId);
}
@Override
public LogbookLifeCycleUnit getLogbookLifeCycleUnit(JsonNode queryDsl, LogbookCollections collection)
throws LogbookDatabaseException, LogbookNotFoundException {
return (LogbookLifeCycleUnit) getLogbook(collection, queryDsl.findValue(
LogbookMongoDbName.objectIdentifier.getDbname()).asText());
}
@Override
public LogbookLifeCycleObjectGroup getLogbookLifeCycleObjectGroup(String objectGroupId)
throws LogbookDatabaseException, LogbookNotFoundException {
return (LogbookLifeCycleObjectGroup) getLogbook(LogbookCollections.LIFECYCLE_OBJECTGROUP, objectGroupId);
}
@SuppressWarnings("rawtypes")
final VitamDocument getLogbookPerOperation(final LogbookCollections collection, String idOperation, String id)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(LIFECYCLE_ITEM, idOperation, id);
VitamDocument lifecycle = null;
try {
lifecycle = (VitamDocument) collection.getCollection().find(and(eq(LogbookDocument.ID, id),
or(eq(LogbookMongoDbName.eventIdentifierProcess.getDbname(), idOperation),
eq(LogbookDocument.EVENTS + '.' + LogbookMongoDbName.eventIdentifierProcess.getDbname(),
idOperation))))
.first();
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(SELECT_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
SELECT_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
}
if (lifecycle == null) {
throw new LogbookNotFoundException(LOGBOOK_LIFE_CYCLE_NOT_FOUND);
}
return lifecycle;
}
@Override
public LogbookLifeCycleUnit getLogbookLifeCycleUnit(String idOperation, String unitId)
throws LogbookDatabaseException, LogbookNotFoundException {
return (LogbookLifeCycleUnit) getLogbookPerOperation(LogbookCollections.LIFECYCLE_UNIT, idOperation, unitId);
}
@Override
public LogbookLifeCycleObjectGroup getLogbookLifeCycleObjectGroup(String idOperation, String objectGroupId)
throws LogbookDatabaseException, LogbookNotFoundException {
return (LogbookLifeCycleObjectGroup) getLogbookPerOperation(LogbookCollections.LIFECYCLE_OBJECTGROUP,
idOperation, objectGroupId);
}
/**
* Internal select
*
* @param collection domain of request
* @param select
* @return the Closeable MongoCursor on the find request based on the given collection
* @throws LogbookException
*/
@SuppressWarnings("rawtypes")
private final MongoCursor select(final LogbookCollections collection, final JsonNode select, boolean sliced)
throws LogbookDatabaseException, LogbookNotFoundException {
if (sliced) {
return select(collection, select, DEFAULT_SLICE);
} else {
return select(collection, select, DEFAULT_SLICE_WITH_ALL_EVENTS);
}
}
/**
* Select with slice possibility
*
* @param collection
* @param select
* @param slice may be null
* @return the closeable MongoCursor
* @throws LogbookDatabaseException
* @throws LogbookNotFoundException
*/
@SuppressWarnings("rawtypes")
private final MongoCursor select(final LogbookCollections collection, final JsonNode select, final ObjectNode slice)
throws LogbookDatabaseException, LogbookNotFoundException {
try {
final SelectParserSingle parser = new SelectParserSingle(new LogbookVarNameAdapter());
parser.parse(select);
if (slice == null) {
parser.addProjection(JsonHandler.createObjectNode(), DEFAULT_ALLKEYS);
} else {
parser.addProjection(slice, DEFAULT_ALLKEYS);
}
// FIXME filter on traceability to adapt
if (LogbookCollections.OPERATION.equals(collection) &&
parser.getRequest().getFinalSelect().toString()
.contains(QueryHelper
.eq(LogbookMongoDbName.eventTypeProcess.getDbname(), LogbookTypeProcess.TRACEABILITY.name())
.toString())) {
return findDocumentsElasticsearch(collection, parser);
} else {
return selectExecute(collection, parser);
}
} catch (final InvalidParseOperationException | InvalidCreateOperationException | LogbookException e) {
throw new LogbookDatabaseException(e);
}
}
/**
* @param collection
* @param parser
* @return the Closeable MongoCursor on the find request based on the given collection
* @throws InvalidParseOperationException
*/
@SuppressWarnings("rawtypes")
private MongoCursor selectExecute(final LogbookCollections collection, SelectParserSingle parser)
throws InvalidParseOperationException {
final SelectToMongoDb selectToMongoDb = new SelectToMongoDb(parser);
// FIXME - add a method to VitamDocument to specify if the tenant should be filtered for collection.
// if the collection should not be filtered, then the method should be overridden
Integer tenantId = ParameterHelper.getTenantParameter();
final Bson condition = and(QueryToMongodb.getCommand(selectToMongoDb.getSelect().getQuery()),
eq(VitamDocument.TENANT_ID, tenantId));
final Bson projection = selectToMongoDb.getFinalProjection();
final Bson orderBy = selectToMongoDb.getFinalOrderBy();
final int offset = selectToMongoDb.getFinalOffset();
final int limit = selectToMongoDb.getFinalLimit();
FindIterable<?> find = collection.getCollection().find(condition).skip(offset);
if (projection != null) {
find = find.projection(projection);
}
if (orderBy != null) {
find = find.sort(orderBy);
}
if (limit > 0) {
find = find.limit(limit);
}
return find.iterator();
}
/**
* @param collection
* @return the Closeable MongoCursor on the find request based on the given collection
* @throws InvalidParseOperationException
*/
@SuppressWarnings("rawtypes")
private MongoCursor selectExecute(final LogbookCollections collection, Select select)
throws InvalidParseOperationException {
final SelectParserSingle parser = new SelectParserSingle(new LogbookVarNameAdapter());
parser.parse(select.getFinalSelect());
parser.addProjection(DEFAULT_SLICE_WITH_ALL_EVENTS, DEFAULT_ALLKEYS);
return selectExecute(collection, parser);
}
@SuppressWarnings("rawtypes")
final VitamDocument getDocument(LogbookParameters item) {
if (item instanceof LogbookOperationParameters) {
return new LogbookOperation((LogbookOperationParameters) item);
} else if (item instanceof LogbookLifeCycleUnitParameters) {
return new LogbookLifeCycleUnitInProcess((LogbookLifeCycleUnitParameters) item);
} else {
return new LogbookLifeCycleObjectGroupInProcess((LogbookLifeCycleObjectGroupParameters) item);
}
}
@SuppressWarnings("rawtypes")
final VitamDocument getDocumentForUpdate(LogbookParameters item) {
if (item instanceof LogbookOperationParameters) {
return new LogbookOperation((LogbookOperationParameters) item, true);
} else if (item instanceof LogbookLifeCycleUnitParameters) {
return new LogbookLifeCycleUnitInProcess((LogbookLifeCycleUnitParameters) item);
} else {
return new LogbookLifeCycleObjectGroupInProcess((LogbookLifeCycleObjectGroupParameters) item);
}
}
@SuppressWarnings("unchecked")
final void createLogbook(LogbookCollections collection, LogbookParameters item)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
ParametersChecker.checkParameter(ITEM_CANNOT_BE_NULL, item);
try {
VitamDocument vitamDocument = getDocument(item);
collection.getCollection().insertOne(vitamDocument);
// FIXME : to be refactor when other collection are indexed in ES
if (LogbookCollections.OPERATION.equals(collection)) {
insertIntoElasticsearch(collection, vitamDocument);
}
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case DUPLICATE_KEY:
throw new LogbookAlreadyExistsException(CREATION_ISSUE + ELEMENT_ALREADY_EXISTS, e);
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(CREATION_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
CREATION_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
} catch (final LogbookExecutionException e) {
throw new LogbookDatabaseException(e);
}
}
@Override
public void createLogbookOperation(LogbookOperationParameters operationItem)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
createLogbook(LogbookCollections.OPERATION, operationItem);
}
@Override
public void createLogbookLifeCycleUnit(String idOperation, LogbookLifeCycleUnitParameters lifecycleItem)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
if (!lifecycleItem.getParameterValue(LogbookParameterName.eventIdentifierProcess).equals(idOperation)) {
throw new IllegalArgumentException("Wrong IdOperation set to create the LifeCycle");
}
createLogbook(LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS, lifecycleItem);
}
@Override
public void createLogbookLifeCycleObjectGroup(String idOperation,
LogbookLifeCycleObjectGroupParameters lifecycleItem)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
if (!lifecycleItem.getParameterValue(LogbookParameterName.eventIdentifierProcess).equals(idOperation)) {
throw new IllegalArgumentException("Wrong IdOperation set to create the LifeCycle");
}
createLogbook(LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS, lifecycleItem);
}
final void updateLogbook(LogbookCollections collection, LogbookParameters item)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(ITEM_CANNOT_BE_NULL, item);
@SuppressWarnings("rawtypes")
final VitamDocument document = getDocumentForUpdate(item);
try {
// Save the _id content before removing it
final String mainLogbookDocumentId = document.getId();
// Remove _id and events fields
document.remove(LogbookDocument.EVENTS);
document.remove(LogbookDocument.ID);
final UpdateResult result = collection.getCollection().updateOne(
eq(LogbookDocument.ID, mainLogbookDocumentId),
Updates.push(LogbookDocument.EVENTS, document));
if (result.getModifiedCount() != 1) {
throw new LogbookNotFoundException(UPDATE_NOT_FOUND_ITEM + mainLogbookDocumentId);
}
// FIXME : to be refactor when other collection are indexed in ES
if (LogbookCollections.OPERATION.equals(collection)) {
updateIntoElasticsearch(collection, mainLogbookDocumentId);
}
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(UPDATE_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
UPDATE_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() + ")",
e);
}
} catch (LogbookExecutionException e) {
throw new LogbookDatabaseException(e);
}
}
private LogbookCollections fromInProcessToProdCollection(LogbookCollections collection) {
if (LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS.equals(collection)) {
return LogbookCollections.LIFECYCLE_UNIT;
} else if (LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS.equals(collection)) {
return LogbookCollections.LIFECYCLE_OBJECTGROUP;
}
return null;
}
@SuppressWarnings({"rawtypes"})
private void updateLogbookLifeCycle(LogbookCollections inProcessCollection, LogbookParameters... parameters)
throws LogbookDatabaseException, LogbookNotFoundException, LogbookAlreadyExistsException {
ParametersChecker.checkParameter(ITEM_CANNOT_BE_NULL, inProcessCollection);
if (parameters == null || parameters.length == 0) {
throw new IllegalArgumentException(AT_LEAST_ONE_ITEM_IS_NEEDED);
}
LogbookTypeProcess processMode = parameters[0].getTypeProcess();
String objectId = parameters[0].getParameterValue(LogbookParameterName.objectIdentifier);
LogbookCollections prodCollection = fromInProcessToProdCollection(inProcessCollection);
// 1- Check if it exists in Production Collection before proceeding to update
LogbookLifeCycle lifeCycleInProd = null;
try {
lifeCycleInProd = (LogbookLifeCycle) getLogbook(prodCollection, objectId);
} catch (LogbookNotFoundException e) {
if (LogbookTypeProcess.UPDATE.equals(processMode)) {
throw e;
}
}
if (lifeCycleInProd != null) {
// Check if there are other operations in process for the given item
LogbookLifeCycle lifeCycleInProcess = null;
try {
lifeCycleInProcess = (LogbookLifeCycle) getLogbook(inProcessCollection, objectId);
} catch (LogbookNotFoundException e) {
LOGGER.info(INIT_UPDATE_LIFECYCLE);
}
if (lifeCycleInProcess == null) {
// This is the first and the only operation on the current object
// So add an element in temporary collection
// Copy the main part of the lifeCycle saved on production collection and append given parameters to
// events
createLogbookLifeCycleForUpdate(inProcessCollection, lifeCycleInProd);
}
}
// Update the temporary lifeCycle
updateBulkLogbook(inProcessCollection, parameters);
}
@Override
public void updateLogbookOperation(LogbookOperationParameters operationItem)
throws LogbookDatabaseException, LogbookNotFoundException {
updateLogbook(LogbookCollections.OPERATION, operationItem);
}
@Override
public void updateLogbookLifeCycleUnit(String idOperation, LogbookLifeCycleUnitParameters lifecycleItem)
throws LogbookDatabaseException, LogbookNotFoundException, LogbookAlreadyExistsException {
if (!lifecycleItem.getParameterValue(LogbookParameterName.eventIdentifierProcess).equals(idOperation)) {
throw new IllegalArgumentException("Wrong IdOperation set to update the LifeCycle");
}
updateLogbookLifeCycle(LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS, lifecycleItem);
}
@Override
public void updateLogbookLifeCycleObjectGroup(String idOperation,
LogbookLifeCycleObjectGroupParameters lifecycleItem)
throws LogbookDatabaseException, LogbookNotFoundException, LogbookAlreadyExistsException {
if (!lifecycleItem.getParameterValue(LogbookParameterName.eventIdentifierProcess).equals(idOperation)) {
throw new IllegalArgumentException("Wrong IdOperation set to update the LifeCycle");
}
updateLogbookLifeCycle(LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS, lifecycleItem);
}
final void rollbackLogbookLifeCycle(LogbookCollections collection, String idOperation, String lifecycleItem)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(LIFECYCLE_ITEM, lifecycleItem);
try {
final DeleteResult result = collection.getCollection().deleteOne(
and(eq(LogbookDocument.ID, lifecycleItem),
or(eq(LogbookMongoDbName.eventIdentifierProcess.getDbname(), idOperation),
eq(LogbookDocument.EVENTS + '.' + LogbookMongoDbName.eventIdentifierProcess.getDbname(),
idOperation))));
if (result.getDeletedCount() != 1) {
throw new LogbookNotFoundException(ROLLBACK_ISSUE + " not found: " + lifecycleItem);
}
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(ROLLBACK_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
ROLLBACK_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
}
}
@Override
public void rollbackLogbookLifeCycleUnit(String idOperation, String lifecycleItem)
throws LogbookDatabaseException, LogbookNotFoundException {
rollbackLogbookLifeCycle(LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS, idOperation, lifecycleItem);
}
@Override
public void rollbackLogbookLifeCycleObjectGroup(String idOperation, String lifecycleItem)
throws LogbookDatabaseException, LogbookNotFoundException {
rollbackLogbookLifeCycle(LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS, idOperation, lifecycleItem);
}
@SuppressWarnings({"unchecked", "rawtypes"})
final void createBulkLogbook(LogbookCollections collection, final LogbookParameters... items)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
if (items == null || items.length == 0) {
throw new IllegalArgumentException(AT_LEAST_ONE_ITEM_IS_NEEDED);
}
int i = 0;
final VitamDocument document = getDocument(items[i]);
final List<VitamDocument> events = new ArrayList<>(items.length - 1);
for (i = 1; i < items.length; i++) {
final VitamDocument currentEvent = getDocumentForUpdate(items[i]);
currentEvent.remove(LogbookDocument.EVENTS);
currentEvent.remove(LogbookDocument.ID);
events.add(currentEvent);
}
document.append(LogbookDocument.EVENTS, events);
try {
collection.getCollection().insertOne(document);
// FIXME : to be refactor when other collection are indexed in ES
if (LogbookCollections.OPERATION.equals(collection)) {
insertIntoElasticsearch(collection, document);
}
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case DUPLICATE_KEY:
throw new LogbookAlreadyExistsException(CREATION_ISSUE + ELEMENT_ALREADY_EXISTS, e);
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(CREATION_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
CREATION_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
} catch (final LogbookExecutionException e) {
throw new LogbookDatabaseException(e);
}
}
@Override
public final void createBulkLogbookOperation(final LogbookOperationParameters... operationItems)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
createBulkLogbook(LogbookCollections.OPERATION, operationItems);
}
@Override
public final void createBulkLogbookLifeCycleUnit(final LogbookLifeCycleUnitParameters... lifecycleItems)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
createBulkLogbook(LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS, lifecycleItems);
}
@Override
public final void createBulkLogbookLifeCycleObjectGroup(
final LogbookLifeCycleObjectGroupParameters... lifecycleItems)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
createBulkLogbook(LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS, lifecycleItems);
}
@SuppressWarnings("rawtypes")
final void updateBulkLogbook(final LogbookCollections collection, final LogbookParameters... items)
throws LogbookDatabaseException, LogbookNotFoundException {
if (items == null || items.length == 0) {
throw new IllegalArgumentException(AT_LEAST_ONE_ITEM_IS_NEEDED);
}
final List<VitamDocument> events = new ArrayList<>(items.length);
// Get the first event to preserve the _id field value
final VitamDocument firstEvent = getDocumentForUpdate(items[0]);
final String mainLogbookDocumentId = firstEvent.getId();
firstEvent.remove(LogbookDocument.EVENTS);
firstEvent.remove(LogbookDocument.ID);
events.add(firstEvent);
for (int i = 1; i < items.length; i++) {
// Remove _id and events fields
final VitamDocument currentEvent = getDocument(items[i]);
currentEvent.remove(LogbookDocument.EVENTS);
currentEvent.remove(LogbookDocument.ID);
events.add(currentEvent);
}
try {
final UpdateResult result = collection.getCollection().updateOne(
eq(LogbookDocument.ID, mainLogbookDocumentId),
Updates.pushEach(LogbookDocument.EVENTS, events));
if (result.getModifiedCount() != 1) {
throw new LogbookNotFoundException(UPDATE_NOT_FOUND_ITEM + mainLogbookDocumentId);
}
// FIXME : to be refactor when other collection are indexed in ES
if (LogbookCollections.OPERATION.equals(collection)) {
updateIntoElasticsearch(collection, mainLogbookDocumentId);
}
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(UPDATE_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
UPDATE_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() + ")",
e);
}
} catch (final LogbookExecutionException e) {
throw new LogbookDatabaseException(e);
}
}
@Override
public final void updateBulkLogbookOperation(final LogbookOperationParameters... operationItems)
throws LogbookDatabaseException, LogbookNotFoundException {
updateBulkLogbook(LogbookCollections.OPERATION, operationItems);
}
@Override
public void updateBulkLogbookLifeCycleUnit(LogbookLifeCycleUnitParameters... lifecycleItems)
throws LogbookDatabaseException, LogbookNotFoundException, LogbookAlreadyExistsException {
updateLogbookLifeCycle(LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS, lifecycleItems);
}
@Override
public void updateBulkLogbookLifeCycleObjectGroup(LogbookLifeCycleObjectGroupParameters... lifecycleItems)
throws LogbookDatabaseException, LogbookNotFoundException, LogbookAlreadyExistsException {
updateLogbookLifeCycle(LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS, lifecycleItems);
}
// Not check, test feature !
@Override
public void deleteCollection(LogbookCollections collection) throws DatabaseException {
Integer tenantId = VitamThreadUtils.getVitamSession().getTenantId();
final long count = collection.getCollection().count();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(collection.getName() + " count before: " + count);
}
if (count > 0) {
final DeleteResult result = collection.getCollection().deleteMany(new Document());
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(collection.getName() + " result.result.getDeletedCount(): " + result.getDeletedCount());
}
if (LogbookCollections.OPERATION.equals(collection)) {
esClient.deleteIndex(LogbookCollections.OPERATION, tenantId);
esClient.addIndex(LogbookCollections.OPERATION, tenantId);
}
if (result.getDeletedCount() != count) {
throw new DatabaseException(String.format("%s: Delete %s from %s elements", collection.getName(), result
.getDeletedCount(), count));
}
}
}
@Override
public LogbookLifeCycleUnitInProcess getLogbookLifeCycleUnitInProcess(String unitId)
throws LogbookDatabaseException, LogbookNotFoundException {
return (LogbookLifeCycleUnitInProcess) getLogbook(LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS, unitId);
}
@Override
public LogbookLifeCycleObjectGroupInProcess getLogbookLifeCycleObjectGroupInProcess(String objectGroupId)
throws LogbookDatabaseException, LogbookNotFoundException {
return (LogbookLifeCycleObjectGroupInProcess) getLogbook(LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS,
objectGroupId);
}
@SuppressWarnings("unchecked")
@Override
public void createLogbookLifeCycleUnit(
LogbookLifeCycleUnitInProcess logbookLifeCycleUnitInProcess)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
// Create Unit lifeCycle from LogbookLifeCycleUnitInProcess instance
ParametersChecker.checkParameter(ITEM_CANNOT_BE_NULL, logbookLifeCycleUnitInProcess);
try {
LogbookLifeCycleUnit logbookLifeCycleUnit =
new LogbookLifeCycleUnit(logbookLifeCycleUnitInProcess.toJson());
LogbookCollections.LIFECYCLE_UNIT.getCollection()
.insertOne(logbookLifeCycleUnit);
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case DUPLICATE_KEY:
throw new LogbookAlreadyExistsException(CREATION_ISSUE + ELEMENT_ALREADY_EXISTS, e);
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(CREATION_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
CREATION_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
}
}
@SuppressWarnings("unchecked")
@Override
public void createLogbookLifeCycleObjectGroup(
LogbookLifeCycleObjectGroupInProcess logbookLifeCycleObjectGrouptInProcess)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
// Create ObjectGRoup lifeCycle from LogbookLifeCycleObjectGroupInProcess instance
ParametersChecker.checkParameter(ITEM_CANNOT_BE_NULL, logbookLifeCycleObjectGrouptInProcess);
try {
LogbookLifeCycleObjectGroup logbookLifeCycleObjectGroup =
new LogbookLifeCycleObjectGroup(logbookLifeCycleObjectGrouptInProcess.toJson());
LogbookCollections.LIFECYCLE_OBJECTGROUP.getCollection()
.insertOne(logbookLifeCycleObjectGroup);
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case DUPLICATE_KEY:
throw new LogbookAlreadyExistsException(CREATION_ISSUE + ELEMENT_ALREADY_EXISTS, e);
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(CREATION_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
CREATION_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
}
}
private void rollBackLifeCyclesByOperation(LogbookCollections isProcessCollection, String operationId)
throws LogbookNotFoundException, LogbookDatabaseException {
ParametersChecker.checkParameter(OPERATION_ITEM, operationId);
try {
// 1- Delete temporary lifeCycles
DeleteResult result = isProcessCollection.getCollection()
.deleteMany(or(eq(LogbookMongoDbName.eventIdentifierProcess.getDbname(), operationId),
eq(LogbookDocument.EVENTS + '.' + LogbookMongoDbName.eventIdentifierProcess.getDbname(),
operationId)));
if (result.getDeletedCount() == 0) {
throw new LogbookNotFoundException(ROLLBACK_ISSUE + " not found: " + operationId);
}
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(ROLLBACK_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
ROLLBACK_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public void createLogbookLifeCycleForUpdate(LogbookCollections inProccessCollection,
LogbookLifeCycle logbookLifeCycleInProd)
throws LogbookDatabaseException, LogbookAlreadyExistsException {
// Create Unit lifeCycle from LogbookLifeCycleUnitInProcess instance
ParametersChecker.checkParameter(ITEM_CANNOT_BE_NULL, logbookLifeCycleInProd);
try {
LogbookLifeCycle logbookLifeCycleInProcess = null;
if (LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS.equals(inProccessCollection)) {
logbookLifeCycleInProcess =
new LogbookLifeCycleUnitInProcess(logbookLifeCycleInProd.toJson());
} else if (LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS.equals(inProccessCollection)) {
logbookLifeCycleInProcess =
new LogbookLifeCycleObjectGroup(logbookLifeCycleInProd.toJson());
}
if (logbookLifeCycleInProcess == null) {
throw new LogbookDatabaseException(CREATION_ISSUE + TIMEOUT_OPERATION);
}
logbookLifeCycleInProcess.remove(LogbookDocument.EVENTS);
logbookLifeCycleInProcess.append(LogbookLifeCycleMongoDbName.eventTypeProcess.getDbname(),
LogbookTypeProcess.UPDATE.toString());
logbookLifeCycleInProcess.append(LogbookDocument.EVENTS, Arrays.asList(new String[0]));
inProccessCollection.getCollection()
.insertOne(logbookLifeCycleInProcess);
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case DUPLICATE_KEY:
throw new LogbookAlreadyExistsException(CREATION_ISSUE + ELEMENT_ALREADY_EXISTS, e);
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(CREATION_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
CREATION_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() +
")",
e);
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public void updateLogbookLifeCycleUnit(LogbookLifeCycleUnitInProcess logbookLifeCycleUnitInProcess)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(ITEM_CANNOT_BE_NULL, logbookLifeCycleUnitInProcess);
String logbookLifeCycleId = logbookLifeCycleUnitInProcess.getId();
try {
final UpdateResult result = LogbookCollections.LIFECYCLE_UNIT.getCollection().updateOne(
eq(LogbookDocument.ID, logbookLifeCycleId),
Updates.pushEach(LogbookDocument.EVENTS,
(List<VitamDocument>) logbookLifeCycleUnitInProcess.get(LogbookDocument.EVENTS)));
if (result.getModifiedCount() != 1) {
throw new LogbookNotFoundException(UPDATE_NOT_FOUND_ITEM + logbookLifeCycleId);
}
// Do not delete the temporary lifeCycle when it is on an INGEST process
List<VitamDocument> newEvents =
(List<VitamDocument>) logbookLifeCycleUnitInProcess.get(LogbookDocument.EVENTS);
if (newEvents != null && newEvents.size() != 0) {
LogbookTypeProcess typeProcess = LogbookTypeProcess
.valueOf(
((Document) newEvents.get(0))
.get(LogbookLifeCycleMongoDbName.eventTypeProcess.getDbname())
.toString());
if (!LogbookTypeProcess.INGEST.equals(typeProcess)) {
// Delete the temporary lifeCycle
LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS.getCollection()
.deleteOne(logbookLifeCycleUnitInProcess);
}
}
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(UPDATE_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
UPDATE_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() + ")",
e);
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public void updateLogbookLifeCycleObjectGroup(
LogbookLifeCycleObjectGroupInProcess logbookLifeCycleObjectGrouptInProcess)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(ITEM_CANNOT_BE_NULL, logbookLifeCycleObjectGrouptInProcess);
String logbookLifeCycleId = logbookLifeCycleObjectGrouptInProcess.getId();
try {
final UpdateResult result = LogbookCollections.LIFECYCLE_OBJECTGROUP.getCollection().updateOne(
eq(LogbookDocument.ID, logbookLifeCycleId),
Updates.pushEach(LogbookDocument.EVENTS,
(List<VitamDocument>) logbookLifeCycleObjectGrouptInProcess.get(LogbookDocument.EVENTS)));
if (result.getModifiedCount() != 1) {
throw new LogbookNotFoundException(UPDATE_NOT_FOUND_ITEM + logbookLifeCycleId);
}
// Do not delete the temporary lifeCycle when it is on an INGEST process
List<VitamDocument> newEvents =
(List<VitamDocument>) logbookLifeCycleObjectGrouptInProcess.get(LogbookDocument.EVENTS);
if (newEvents != null && newEvents.size() != 0) {
LogbookTypeProcess typeProcess = LogbookTypeProcess
.valueOf(
((Document) newEvents.get(0))
.get(LogbookLifeCycleMongoDbName.eventTypeProcess.getDbname())
.toString());
if (!LogbookTypeProcess.INGEST.equals(typeProcess)) {
// Delete the temporary lifeCycle
LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS.getCollection()
.deleteOne(logbookLifeCycleObjectGrouptInProcess);
}
}
} catch (final MongoException e) {
switch (getErrorCategory(e)) {
case EXECUTION_TIMEOUT:
throw new LogbookDatabaseException(UPDATE_ISSUE + TIMEOUT_OPERATION, e);
case UNCATEGORIZED:
default:
throw new LogbookDatabaseException(
UPDATE_ISSUE + " (" + e.getClass().getName() + " " + e.getMessage() + ": " + e.getCode() + ")",
e);
}
}
}
@Override
public void rollBackUnitLifeCyclesByOperation(String operationId)
throws LogbookNotFoundException, LogbookDatabaseException {
rollBackLifeCyclesByOperation(LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS, operationId);
}
@Override
public void rollBackObjectGroupLifeCyclesByOperation(String operationId)
throws LogbookNotFoundException, LogbookDatabaseException {
rollBackLifeCyclesByOperation(LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS, operationId);
}
@Override
public boolean existsLogbookLifeCycleUnitInProcess(String unitId)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(LIFECYCLE_ITEM, unitId);
return exists(LogbookCollections.LIFECYCLE_UNIT_IN_PROCESS, unitId);
}
@Override
public boolean existsLogbookLifeCycleObjectGroupInProcess(String objectGroupId)
throws LogbookDatabaseException, LogbookNotFoundException {
ParametersChecker.checkParameter(LIFECYCLE_ITEM, objectGroupId);
return exists(LogbookCollections.LIFECYCLE_OBJECTGROUP_IN_PROCESS, objectGroupId);
}
/**
* Search in elastic search then get object detail in MongoDb.
*
* @param collection the collection
* @param parser the parser containing the query
* @return the cursor on the result datas
* @throws InvalidParseOperationException if the MongoDb query can't be translated to ES a valid query
* @throws InvalidCreateOperationException if a MongoDb query can't be created from ES results
* @throws LogbookException if an exception occured while executing the ES query
*/
private MongoCursor<?> findDocumentsElasticsearch(LogbookCollections collection,
SelectParserSingle parser)
throws InvalidParseOperationException, InvalidCreateOperationException, LogbookException {
Integer tenantId = VitamThreadUtils.getVitamSession().getTenantId();
final SelectToMongodb requestToMongodb = new SelectToMongodb(parser);
QueryBuilder query = QueryToElasticsearch.getCommand(requestToMongodb.getNthQuery(0));
List<SortBuilder> sorts = QueryToElasticsearch.getSorts(requestToMongodb.getFinalOrderBy());
SearchResponse elasticSearchResponse =
collection.getEsClient().search(collection, tenantId, query, null, sorts, requestToMongodb.getFinalOffset(),
requestToMongodb.getFinalLimit());
if (elasticSearchResponse.status() != RestStatus.OK) {
return new EmptyMongoCursor();
}
final SearchHits hits = elasticSearchResponse.getHits();
if (hits.getTotalHits() == 0) {
return new EmptyMongoCursor();
}
final Iterator<SearchHit> iterator = hits.iterator();
final BooleanQuery newQuery = or();
// get document with Elasticsearch then create a new request to mongodb with unique object's attribute
while (iterator.hasNext()) {
final SearchHit hit = iterator.next();
final Map<String, Object> src = hit.getSource();
LOGGER.debug("findDocumentsElasticsearch result" + src.toString());
if (src.get(LogbookMongoDbName.eventIdentifierProcess.getDbname()) != null) {
newQuery.add(QueryHelper.eq(LogbookMongoDbName.eventIdentifierProcess.getDbname(),
src.get(LogbookMongoDbName.eventIdentifierProcess.getDbname()).toString()));
}
}
// replace query with list of ids from es
parser.getRequest().setQuery(newQuery);
return selectExecute(collection, parser);
}
/**
* Insert a new document in ES.
*
* @param collection the collection
* @param vitamDocument the document to save in ES
* @throws LogbookExecutionException if the ES insert was in error
*/
private void insertIntoElasticsearch(LogbookCollections collection, VitamDocument vitamDocument)
throws LogbookExecutionException {
Integer tenantId = VitamThreadUtils.getVitamSession().getTenantId();
LOGGER.debug("insertToElasticsearch");
Map<String, String> mapIdJson = new HashMap<>();
String id = vitamDocument.getId();
vitamDocument.remove(LogbookCollections.ID);
tranformEvDetDataForElastic(vitamDocument);
final String mongoJson = vitamDocument.toJson(new JsonWriterSettings(JsonMode.STRICT));
vitamDocument.clear();
final String esJson = ((DBObject) com.mongodb.util.JSON.parse(mongoJson)).toString();
mapIdJson.put(id, esJson);
final BulkResponse bulkResponse = collection.getEsClient().addEntryIndexes(collection, tenantId, mapIdJson);
if (bulkResponse.hasFailures()) {
throw new LogbookExecutionException("Index Elasticsearch has errors");
}
}
/**
* Update a document in ES by loading its value in mongodb and saving it in it's corresponding ES index.
*
* @param collection the collection
* @param id the id of the document to update
* @throws LogbookExecutionException if the ES update was in error
* @throws LogbookNotFoundException if the document was not found in mongodb
*/
private void updateIntoElasticsearch(LogbookCollections collection, String id)
throws LogbookExecutionException, LogbookNotFoundException {
Integer tenantId = VitamThreadUtils.getVitamSession().getTenantId();
LOGGER.debug("updateIntoElasticsearch");
VitamDocument existingDocument =
(VitamDocument) collection.getCollection().find(eq(VitamDocument.ID, id)).first();
if (existingDocument == null) {
throw new LogbookNotFoundException("Logbook item not found");
}
existingDocument.remove(LogbookCollections.ID);
tranformEvDetDataForElastic(existingDocument);
final String mongoJson = existingDocument.toJson(new JsonWriterSettings(JsonMode.STRICT));
existingDocument.clear();
final String esJson = ((DBObject) com.mongodb.util.JSON.parse(mongoJson)).toString();
final boolean response = collection.getEsClient().updateEntryIndex(collection, tenantId, id, esJson);
if (response == false) {
throw new LogbookExecutionException("Update Elasticsearch has errors");
}
}
/**
* Replace the "evDetData" value in the document and the sub-events from a string by a json object
*
* @param vitamDocument logbook vitam document
*/
private void tranformEvDetDataForElastic(VitamDocument vitamDocument) {
if (vitamDocument.get(LogbookMongoDbName.eventDetailData.getDbname()) != null) {
String evDetDataString = (String) vitamDocument.get(LogbookMongoDbName.eventDetailData.getDbname());
LOGGER.error(evDetDataString);
try {
JsonNode evDetData = JsonHandler.getFromString(evDetDataString);
vitamDocument.remove(LogbookMongoDbName.eventDetailData.getDbname());
vitamDocument.put(LogbookMongoDbName.eventDetailData.getDbname(), evDetData);
} catch (InvalidParseOperationException e) {
LOGGER.warn("EvDetData is not a json compatible field");
}
}
List<Document> eventDocuments = (List<Document>) vitamDocument.get(LogbookDocument.EVENTS);
if (eventDocuments != null) {
for (Document eventDocument : eventDocuments) {
if (eventDocument.getString(LogbookMongoDbName.eventDetailData.getDbname()) != null) {
String eventEvDetDataString =
eventDocument.getString(LogbookMongoDbName.eventDetailData.getDbname());
Document eventEvDetDataDocument = Document.parse(eventEvDetDataString);
eventDocument.remove(LogbookMongoDbName.eventDetailData.getDbname());
eventDocument.put(LogbookMongoDbName.eventDetailData.getDbname(), eventEvDetDataDocument);
}
}
}
vitamDocument.remove(LogbookDocument.EVENTS);
vitamDocument.put(LogbookDocument.EVENTS, eventDocuments);
}
}