package com.constellio.sdk.load.script;
import static com.constellio.data.dao.dto.records.OptimisticLockingResolution.EXCEPTION;
import static com.constellio.model.entities.records.wrappers.EventType.VIEW;
import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from;
import static java.util.Arrays.asList;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
import demo.DemoInitScript;
import org.apache.commons.io.IOUtils;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import com.constellio.app.entities.modules.InstallableModule;
import com.constellio.app.modules.rm.ConstellioRMModule;
import com.constellio.app.modules.rm.constants.RMRoles;
import com.constellio.app.modules.rm.model.enums.CopyType;
import com.constellio.app.modules.rm.services.RMSchemasRecordsServices;
import com.constellio.app.modules.rm.wrappers.Document;
import com.constellio.app.modules.rm.wrappers.Folder;
import com.constellio.app.services.factories.AppLayerFactory;
import com.constellio.data.dao.dto.records.RecordsFlushing;
import com.constellio.data.dao.services.bigVault.solr.BigVaultServer;
import com.constellio.data.dao.services.bigVault.solr.BigVaultServerTransaction;
import com.constellio.data.io.services.facades.IOServices;
import com.constellio.data.utils.BigFileEntry;
import com.constellio.data.utils.BigFileFolderIterator;
import com.constellio.data.utils.ThreadList;
import com.constellio.model.entities.records.Record;
import com.constellio.model.entities.records.Transaction;
import com.constellio.model.entities.records.wrappers.RecordWrapper;
import com.constellio.model.entities.records.wrappers.User;
import com.constellio.model.entities.schemas.Schemas;
import com.constellio.model.entities.security.global.GlobalGroup;
import com.constellio.model.entities.security.global.UserCredential;
import com.constellio.model.services.contents.ContentManager;
import com.constellio.model.services.contents.ContentVersionDataSummary;
import com.constellio.model.services.factories.ModelLayerFactory;
import com.constellio.model.services.logging.EventFactory;
import com.constellio.model.services.records.BulkRecordTransactionHandler;
import com.constellio.model.services.records.BulkRecordTransactionHandlerOptions;
import com.constellio.model.services.records.RecordServices;
import com.constellio.model.services.search.SearchServices;
import com.constellio.model.services.search.query.logical.LogicalSearchQuery;
import com.constellio.model.services.security.authentification.PasswordFileAuthenticationService;
import com.constellio.model.services.users.UserServices;
import com.constellio.sdk.load.script.utils.LinkableIdsList;
import com.constellio.sdk.load.script.utils.LinkableRecordsList;
public class SystemWithDataAndRMModuleScript implements DemoInitScript {
static int documentsBatch = 100;
File bigFilesFolder;
int subFoldersPerFolder = 0;
int subSubFoldersPerFolder = 0;
int numberOfRootFolders = 0;
int numberOfDocuments = 0;
List<String> collections = new ArrayList<>();
BigFileFolderIterator contentIterator;
UserPreparator userPreparator;
TaxonomyPreparator categoriesTaxonomy;
PrincipalTaxonomyPreparator administrativeUnitsTaxonomy;
public File getBigFilesFolder() {
return bigFilesFolder;
}
public void setBigFilesFolder(File bigFilesFolder) {
this.bigFilesFolder = bigFilesFolder;
}
public int getNumberOfRootFolders() {
return numberOfRootFolders;
}
public void setNumberOfRootFolders(int numberOfRootFolders) {
this.numberOfRootFolders = numberOfRootFolders;
}
public UserPreparator getUserPreparator() {
return userPreparator;
}
public void setUserPreparator(UserPreparator userPreparator) {
this.userPreparator = userPreparator;
}
public int getSubFoldersPerFolder() {
return subFoldersPerFolder;
}
public void setSubFoldersPerFolder(int subFoldersPerFolder) {
this.subFoldersPerFolder = subFoldersPerFolder;
}
public int getSubSubFoldersPerFolder() {
return subSubFoldersPerFolder;
}
public void setSubSubFoldersPerFolder(int subSubFoldersPerFolder) {
this.subSubFoldersPerFolder = subSubFoldersPerFolder;
}
public int getNumberOfDocuments() {
return numberOfDocuments;
}
public void setNumberOfDocuments(int numberOfDocuments) {
this.numberOfDocuments = numberOfDocuments;
}
public List<String> getCollections() {
return collections;
}
public void setCollections(List<String> collections) {
this.collections = collections;
}
public TaxonomyPreparator getCategoriesTaxonomy() {
return categoriesTaxonomy;
}
public void setCategoriesTaxonomy(TaxonomyPreparator categoriesTaxonomy) {
this.categoriesTaxonomy = categoriesTaxonomy;
}
public PrincipalTaxonomyPreparator getAdministrativeUnitsTaxonomy() {
return administrativeUnitsTaxonomy;
}
public void setAdministrativeUnitsTaxonomy(PrincipalTaxonomyPreparator administrativeUnitsTaxonomy) {
this.administrativeUnitsTaxonomy = administrativeUnitsTaxonomy;
}
@Override
public void setup(AppLayerFactory appLayerFactory, ModelLayerFactory modelLayerFactory)
throws Exception {
if (collections.isEmpty()) {
throw new RuntimeException("Parameter 'collections' is required");
}
if (categoriesTaxonomy == null) {
throw new RuntimeException("Parameter 'categoriesTaxonomy' is required");
}
if (administrativeUnitsTaxonomy == null) {
throw new RuntimeException("Parameter 'administrativeUnitsTaxonomy' is required");
}
for (String collection : collections) {
givenCollectionWithRMModuleAndUsers(appLayerFactory, collection, collection);
}
IOServices ioServices = modelLayerFactory.getIOServicesFactory().newIOServices();
contentIterator = new BigFileFolderIterator(bigFilesFolder, ioServices, "bigFileITerator");
List<String> groupCodes = new ArrayList<>();
UserServices userServices = modelLayerFactory.newUserServices();
PasswordFileAuthenticationService authenticationService = modelLayerFactory.getPasswordFileAuthenticationService();
for (GlobalGroup group : userPreparator.createGroups()) {
userServices.addUpdateGlobalGroup(group);
groupCodes.add(group.getCode());
}
for (UserCredential user : userPreparator.createUsers(groupCodes)) {
userServices.addUpdateUserCredential(user);
authenticationService.changePassword(user.getUsername(), "password");
}
for (String collection : collections) {
setupCollection(collection, appLayerFactory);
}
BigVaultServer bigVaultServer = modelLayerFactory.getDataLayerFactory().getRecordsVaultServer();
bigVaultServer.addAll(new BigVaultServerTransaction(RecordsFlushing.NOW()).setDeletedQueries(asList("type_s:marker")));
}
@Override
public List<InstallableModule> getModules() {
ConstellioRMModule rmModule = new ConstellioRMModule();
return asList((InstallableModule) rmModule);
}
private void givenCollectionWithRMModuleAndUsers(AppLayerFactory appLayerFactory, String collection, String title)
throws Exception {
ModelLayerFactory modelLayerFactory = appLayerFactory.getModelLayerFactory();
//CREATE COLLECTION
Record collectionRecord = appLayerFactory.getCollectionsManager()
.createCollectionInCurrentVersion(collection, asList("fr"));
modelLayerFactory.newRecordServices().update(collectionRecord.set(Schemas.TITLE, title));
//SETUP MODULES
appLayerFactory.getModulesManager().enableValidModuleAndGetInvalidOnes(collection, new ConstellioRMModule());
}
private void setupCollection(String collection, AppLayerFactory appLayerFactory)
throws Exception {
ModelLayerFactory modelLayerFactory = appLayerFactory.getModelLayerFactory();
RMSchemasRecordsServices rm = new RMSchemasRecordsServices(collection, appLayerFactory);
User user = modelLayerFactory.newUserServices().getUserInCollection("admin", collection);
modelLayerFactory.newRecordServices().update(user.setUserRoles(asList(RMRoles.RGD)).setCollectionAllAccess(true));
LogicalSearchQuery allUsersQuery = new LogicalSearchQuery(from(rm.userSchemaType()).returnAll());
LogicalSearchQuery allGroupsQuery = new LogicalSearchQuery(from(rm.groupSchemaType()).returnAll());
LogicalSearchQuery allRulesQuery = new LogicalSearchQuery(from(rm.retentionRule.schemaType()).returnAll());
SearchServices searchServices = modelLayerFactory.newSearchServices();
LinkableRecordsList<User> users = new LinkableRecordsList<>(rm.wrapUsers(searchServices.search(allUsersQuery)));
LinkableIdsList userIds = new LinkableIdsList(searchServices.searchRecordIds(allUsersQuery));
LinkableIdsList groupIds = new LinkableIdsList(searchServices.searchRecordIds(allGroupsQuery));
Transaction transaction = new Transaction().setOptimisticLockingResolution(EXCEPTION);
List<RecordWrapper> categories = prepareTaxonomy(rm, transaction, categoriesTaxonomy);
modelLayerFactory.newRecordServices().execute(transaction);
transaction = new Transaction().setOptimisticLockingResolution(EXCEPTION);
List<RecordWrapper> administrativeUnits = prepareTaxonomy(rm, transaction, administrativeUnitsTaxonomy);
modelLayerFactory.newRecordServices().execute(transaction);
for (RecordWrapper recordWrapper : administrativeUnits) {
administrativeUnitsTaxonomy.setupAuthorizations(rm, recordWrapper, userIds, groupIds);
}
LinkableIdsList categoriesIds = LinkableIdsList.forRecords(categories);
LinkableIdsList administrativeUnitsIds = LinkableIdsList.forRecords(administrativeUnits);
String ruleId = modelLayerFactory.newSearchServices().searchRecordIds(allRulesQuery).get(0);
LinkableIdsList folderIds = createFolders(rm, ruleId, categoriesIds, administrativeUnitsIds, users);
createDocuments(rm, folderIds, users);
}
private void createDocuments(RMSchemasRecordsServices rm, LinkableIdsList folderIds, LinkableRecordsList<User> users) {
EventFactory eventFactory = new EventFactory(rm.getModelLayerFactory());
User admin = rm.getModelLayerFactory().newUserServices().getUserInCollection("admin", rm.getCollection());
RecordServices recordServices = rm.getModelLayerFactory().newRecordServices();
BulkRecordTransactionHandlerOptions options = new BulkRecordTransactionHandlerOptions();
options.withRecordsPerBatch(1000);
options.withNumberOfThreads(4);
BulkRecordTransactionHandler transactionHandler = new BulkRecordTransactionHandler(recordServices, "addDocuments",
options);
ThreadList<DocumentSavehread> threadList = new ThreadList<>();
AtomicInteger addedCount = new AtomicInteger();
for(int i = 0 ; i < 10 ; i++) {
threadList.addAndStart(new DocumentSavehread(addedCount, numberOfDocuments, contentIterator, rm, folderIds, users, transactionHandler));
}
try {
threadList.joinAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
transactionHandler.closeAndJoin();
}
private class DocumentSavehread extends Thread {
private int requiredCount;
private AtomicInteger addedCount = new AtomicInteger();
private BigFileFolderIterator bigFileFolderIterator;
private RMSchemasRecordsServices rm ;
private LinkableIdsList folderIds;
private LinkableRecordsList<User> users;
private ContentManager contentManager;
private BulkRecordTransactionHandler transactionHandler;
private EventFactory eventFactory;
private User admin;
public DocumentSavehread(AtomicInteger addedCount, int requiredCount, BigFileFolderIterator bigFileFolderIterator, RMSchemasRecordsServices rm, LinkableIdsList folderIds, LinkableRecordsList<User> users, BulkRecordTransactionHandler transactionHandler) {
this.addedCount = addedCount;
this.requiredCount = requiredCount;
this.bigFileFolderIterator = bigFileFolderIterator;
this.rm = rm;
this.folderIds =folderIds;
this.users = users;
this.transactionHandler = transactionHandler;
this.contentManager = rm.getModelLayerFactory().getContentManager();
this.eventFactory = new EventFactory(rm.getModelLayerFactory());
this.admin = rm.getModelLayerFactory().newUserServices().getUserInCollection("admin", rm.getCollection());
}
@Override
public void run() {
while(addedCount.get() < requiredCount) {
List<BigFileEntry> entries = new ArrayList<>();
Folder folder = rm.getFolder(folderIds.next());
User createdBy = users.next();
synchronized (bigFileFolderIterator) {
for(int i = 0 ; i < documentsBatch ; i++) {
entries.add(bigFileFolderIterator.next());
}
}
List<Record> records = new ArrayList<>();
for(int i = 0 ; i < documentsBatch ; i++) {
BigFileEntry entry = contentIterator.next();
InputStream in = new ByteArrayInputStream(entry.getBytes());
ContentVersionDataSummary contentVersionDataSummary;
try {
contentVersionDataSummary = contentManager.upload(in, false, true, null);
} finally {
IOUtils.closeQuietly(in);
}
Document document = rm.newDocument()
.setFolder(folder)
.setFormCreatedBy(createdBy)
.setFormCreatedOn(new LocalDateTime())
.setTitle(entry.getFileName())
.setContent(contentManager.createMajor(createdBy, entry.getFileName(), contentVersionDataSummary));
records.add(document.getWrappedRecord());
Record documentRecord = document.getWrappedRecord();
records.add(eventFactory.newRecordEvent(documentRecord, users.next(), VIEW).getWrappedRecord());
records.add(eventFactory.newRecordEvent(documentRecord, users.next(), VIEW).getWrappedRecord());
records.add(eventFactory.newRecordEvent(documentRecord, users.next(), VIEW).getWrappedRecord());
}
List<Record> dependOn = asList(folder.getWrappedRecord());
transactionHandler.append(records, dependOn);
addedCount.addAndGet(documentsBatch);
}
}
}
private LinkableIdsList createFolders(RMSchemasRecordsServices rm, String ruleId, LinkableIdsList categoriesIds,
LinkableIdsList administrativeUnitsIds, LinkableRecordsList<User> users) {
Map<String, User> userCache = new HashMap<>();
EventFactory eventFactory = new EventFactory(rm.getModelLayerFactory());
List<String> folderIds = new ArrayList<>();
RecordServices recordServices = rm.getModelLayerFactory().newRecordServices();
BulkRecordTransactionHandlerOptions options = new BulkRecordTransactionHandlerOptions();
//options.withRecordsPerBatch(1 + subFoldersPerFolder + subFoldersPerFolder * subSubFoldersPerFolder);
BulkRecordTransactionHandler transactionHandler = new BulkRecordTransactionHandler(recordServices, "addFolders", options);
for (int i = 0; i < numberOfRootFolders; i++) {
List<Record> foldersToAppend = new ArrayList<>();
Folder folder = rm.newFolder()
.setTitle("Folder " + i)
.setAdministrativeUnitEntered(administrativeUnitsIds.next())
.setOpenDate(new LocalDate())
.setCategoryEntered(categoriesIds.next())
.setRetentionRuleEntered(ruleId)
.setCopyStatusEntered(CopyType.PRINCIPAL);
folderIds.add(folder.getId());
foldersToAppend.add(folder.getWrappedRecord());
for (int j = 0; j < subFoldersPerFolder; j++) {
Folder subFolder = rm.newFolder()
.setTitle("Folder " + i + "-" + j)
.setParentFolder(folder)
.setCopyStatusEntered(CopyType.PRINCIPAL)
.setOpenDate(new LocalDate());
folderIds.add(subFolder.getId());
foldersToAppend.add(subFolder.getWrappedRecord());
for (int k = 0; k < subSubFoldersPerFolder; k++) {
Folder subSubFolder = rm.newFolder()
.setTitle("Folder " + i + "-" + j + "-" + k)
.setParentFolder(subFolder)
.setCopyStatusEntered(CopyType.PRINCIPAL)
.setOpenDate(new LocalDate());
folderIds.add(subSubFolder.getId());
foldersToAppend.add(subSubFolder.getWrappedRecord());
}
}
transactionHandler.append(foldersToAppend);
for (Record folderRecord : foldersToAppend) {
transactionHandler.append(eventFactory.newRecordEvent(folderRecord, users.next(), VIEW).getWrappedRecord());
transactionHandler.append(eventFactory.newRecordEvent(folderRecord, users.next(), VIEW).getWrappedRecord());
transactionHandler.append(eventFactory.newRecordEvent(folderRecord, users.next(), VIEW).getWrappedRecord());
}
}
transactionHandler.closeAndJoin();
return new LinkableIdsList(folderIds);
}
private List<RecordWrapper> prepareTaxonomy(RMSchemasRecordsServices rm, Transaction transaction,
TaxonomyPreparator taxonomy) {
List<RecordWrapper> recordWrappers = new ArrayList<>();
taxonomy.init(rm, transaction);
Stack<Integer> stack = new Stack<>();
List<RecordWrapper> rootConcepts = taxonomy.createRootConcepts(rm);
recordWrappers.addAll(rootConcepts);
for (int i = 0; i < rootConcepts.size(); i++) {
stack.push(i);
RecordWrapper recordWrapper = rootConcepts.get(i);
transaction.add(recordWrapper);
prepareTaxonomy(rm, transaction, recordWrapper, taxonomy, stack, recordWrappers);
stack.pop();
}
return recordWrappers;
}
private void prepareTaxonomy(RMSchemasRecordsServices rm, Transaction transaction, RecordWrapper record,
TaxonomyPreparator taxonomy, Stack<Integer> stack, List<RecordWrapper> recordWrappers) {
List<RecordWrapper> childConcepts = taxonomy.createChildConcepts(rm, record, stack);
recordWrappers.addAll(childConcepts);
for (int i = 0; i < childConcepts.size(); i++) {
stack.push(i);
RecordWrapper recordWrapper = childConcepts.get(i);
transaction.add(recordWrapper);
prepareTaxonomy(rm, transaction, recordWrapper, taxonomy, stack, recordWrappers);
stack.pop();
}
}
}