org.alfresco.bm.dataload.rm.fileplan.ScheduleRecordLoaders.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.bm.dataload.rm.fileplan.ScheduleRecordLoaders.java

Source

/*
 * Copyright (C) 2005-2017 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

package org.alfresco.bm.dataload.rm.fileplan;

import static org.apache.commons.lang3.StringUtils.isNotBlank;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.UUID;

import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;

import org.alfresco.bm.cm.FolderData;
import org.alfresco.bm.dataload.RMBaseEventProcessor;
import org.alfresco.bm.event.Event;
import org.alfresco.bm.event.EventResult;
import org.alfresco.bm.session.SessionService;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Prepare event for loading records in filePlan folder structure.
 *
 * @author Silviu Dinuta
 * @since 2.6
 *
 */
public class ScheduleRecordLoaders extends RMBaseEventProcessor {
    public static final String EVENT_NAME_LOAD_RECORDS = "loadRecords";
    public static final String EVENT_NAME_SCHEDULE_RECORD_LOADERS = "scheduleRecordLoaders";
    public static final String EVENT_NAME_LOADING_COMPLETE = "loadingRecordsComplete";
    public static final String EVENT_NAME_CONTINUE_LOADING_UNFILED_RECORD_FOLDERS = "scheduleUnfiledRecordFoldersLoaders";

    @Autowired
    private SessionService sessionService;

    private int maxActiveLoaders;
    private long loadCheckDelay;
    private boolean uploadRecords;
    private int recordsNumber;
    private String recordFolderPaths;
    private List<String> paths;
    private String eventNameLoadRecords = EVENT_NAME_LOAD_RECORDS;
    private String eventNameScheduleRecordLoaders = EVENT_NAME_SCHEDULE_RECORD_LOADERS;
    private String eventNameLoadingComplete = EVENT_NAME_LOADING_COMPLETE;
    private String eventNameContinueLoadingUnfiledRecordFolders = EVENT_NAME_CONTINUE_LOADING_UNFILED_RECORD_FOLDERS;

    private LinkedHashMap<FolderData, Integer> mapOfRecordsPerRecordFolder = null;

    /**
     * @return the uploadRecords
     */
    public boolean isUploadRecords() {
        return uploadRecords;
    }

    /**
     * @param uploadRecords the uploadRecords to set
     */
    public void setUploadRecords(boolean uploadRecords) {
        this.uploadRecords = uploadRecords;
    }

    /**
     * @return the recordFolderPaths
     */
    public String getRecordFolderPaths() {
        return recordFolderPaths;
    }

    /**
     * @param recordFolderPaths the recordFolderPaths to set
     */
    public void setRecordFolderPaths(String recordFolderPaths) {
        this.recordFolderPaths = recordFolderPaths;
        if (isNotBlank(this.recordFolderPaths)) {
            paths = Arrays.asList(this.recordFolderPaths.split(","));
        }
    }

    /**
     * @return the recordsNumber
     */
    public int getRecordsNumber() {
        return recordsNumber;
    }

    /**
     * @param recordsNumber the recordsNumber to set
     */
    public void setRecordsNumber(int recordsNumber) {
        this.recordsNumber = recordsNumber;
    }

    /**
     * @return the loadCheckDelay
     */
    public long getLoadCheckDelay() {
        return loadCheckDelay;
    }

    /**
     * @param loadCheckDelay the loadCheckDelay to set
     */
    public void setLoadCheckDelay(long loadCheckDelay) {
        this.loadCheckDelay = loadCheckDelay;
    }

    /**
     * @return the maxActiveLoaders
     */
    public int getMaxActiveLoaders() {
        return maxActiveLoaders;
    }

    /**
     * @param maxActiveLoaders the maxActiveLoaders to set
     */
    public void setMaxActiveLoaders(int maxActiveLoaders) {
        this.maxActiveLoaders = maxActiveLoaders;
    }

    /**
     * @return the eventNameLoadRecords
     */
    public String getEventNameLoadRecords() {
        return eventNameLoadRecords;
    }

    /**
     * @param eventNameLoadRecords the eventNameLoadRecords to set
     */
    public void setEventNameLoadRecords(String eventNameLoadRecords) {
        this.eventNameLoadRecords = eventNameLoadRecords;
    }

    /**
     * @return the eventNameScheduleRecordLoaders
     */
    public String getEventNameScheduleRecordLoaders() {
        return eventNameScheduleRecordLoaders;
    }

    /**
     * @param eventNameScheduleRecordLoaders the eventNameScheduleRecordLoaders to set
     */
    public void setEventNameScheduleRecordLoaders(String eventNameScheduleRecordLoaders) {
        this.eventNameScheduleRecordLoaders = eventNameScheduleRecordLoaders;
    }

    /**
     * @return the eventNameLoadingComplete
     */
    public String getEventNameLoadingComplete() {
        return eventNameLoadingComplete;
    }

    /**
     * @param eventNameLoadingComplete the eventNameLoadingComplete to set
     */
    public void setEventNameLoadingComplete(String eventNameLoadingComplete) {
        this.eventNameLoadingComplete = eventNameLoadingComplete;
    }

    public String getEventNameContinueLoadingUnfiledRecordFolders() {
        return eventNameContinueLoadingUnfiledRecordFolders;
    }

    public void setEventNameContinueLoadingUnfiledRecordFolders(
            String eventNameContinueLoadingUnfiledRecordFolders) {
        this.eventNameContinueLoadingUnfiledRecordFolders = eventNameContinueLoadingUnfiledRecordFolders;
    }

    @Override
    protected EventResult processEvent(Event event) throws Exception {
        // Are there still sessions active?
        long sessionCount = sessionService.getActiveSessionsCount();
        int loaderSessionsToCreate = maxActiveLoaders - (int) sessionCount;
        List<Event> nextEvents = new ArrayList<Event>(maxActiveLoaders);

        // Do we actually need to do anything
        if (!isUploadRecords()) {
            return new EventResult(
                    "Uploading of records into File Plan not wanted, continue with loading unfiled record folders structure.",
                    new Event(getEventNameContinueLoadingUnfiledRecordFolders(), null));
        }
        if (recordsNumber > 0) {
            // Prepare Records
            prepareRecords(loaderSessionsToCreate, nextEvents);
        }

        // If there are no events, then we have finished
        String msg = null;
        if (loaderSessionsToCreate > 0 && nextEvents.size() == 0) {
            // There are no records to load even though there are sessions available
            mapOfRecordsPerRecordFolder = null;
            Event nextEvent = new Event(eventNameLoadingComplete, null);
            nextEvents.add(nextEvent);
            msg = "Loading completed.  Raising 'done' event.";
        } else {
            // Reschedule self
            Event nextEvent = new Event(eventNameScheduleRecordLoaders, System.currentTimeMillis() + loadCheckDelay,
                    null);
            nextEvents.add(nextEvent);
            msg = "Raised further " + (nextEvents.size() - 1) + " events and rescheduled self.";
        }

        if (logger.isDebugEnabled()) {
            logger.debug(msg);
        }

        EventResult result = new EventResult(msg, nextEvents);
        return result;
    }

    /**
     * Helper method that initialize the record folders that can receive loaded unfiled records.
     * This method, also calculates the number of records to  add to the initialized record folders.
     */
    private void calculateListOfEmptyFolders() {
        if (mapOfRecordsPerRecordFolder == null) {
            mapOfRecordsPerRecordFolder = new LinkedHashMap<FolderData, Integer>();
            List<FolderData> recordFoldersThatNeedRecords = new ArrayList<FolderData>();
            if (paths == null || paths.size() == 0) {
                // get the existing file plan folder structure
                recordFoldersThatNeedRecords.addAll(initialiseFoldersToExistingStructure(RECORD_FOLDER_CONTEXT));
            } else {
                LinkedHashSet<FolderData> structureFromExistentProvidedPaths = new LinkedHashSet<FolderData>();
                for (String path : paths) {
                    if (!path.startsWith("/")) {
                        path = "/" + path;
                    }
                    //if the path is category and exists
                    FolderData folder = fileFolderService.getFolder(RECORD_CATEGORY_CONTEXT,
                            RECORD_CONTAINER_PATH + path);
                    if (folder == null)//if folder is not a category verify if it is a record folder and exists
                    {
                        folder = fileFolderService.getFolder(RECORD_FOLDER_CONTEXT, RECORD_CONTAINER_PATH + path);
                    }
                    if (folder != null)// if folder exists
                    {
                        structureFromExistentProvidedPaths.addAll(getRecordFolders(folder));
                    } else {
                        try {
                            folder = createFolder(path);
                            recordFoldersThatNeedRecords.add(folder);
                        } catch (Exception e) {
                            // something went wrong on creating current path structure, not all required paths will be created
                        }
                    }
                }
                // add record folders from existent paths
                if (structureFromExistentProvidedPaths.size() > 0) {
                    recordFoldersThatNeedRecords.addAll(structureFromExistentProvidedPaths);
                }
                // configured paths did not existed in db and something went wrong with creation for all of them,
                // initialize to existing structure in this case
                if (recordFoldersThatNeedRecords.size() == 0) {
                    recordFoldersThatNeedRecords
                            .addAll(initialiseFoldersToExistingStructure(RECORD_FOLDER_CONTEXT));
                }
            }
            if (recordFoldersThatNeedRecords.size() > 0) {
                mapOfRecordsPerRecordFolder = distributeNumberOfRecords(recordFoldersThatNeedRecords,
                        recordsNumber);
            }
        }
    }

    /**
     * Helper method used for creating in alfresco repo and in mongo DB, record root categories, record categories and record folders from configured path elements.
     *
     * @param path - path element
     * @return created record folder, or existent record folder, if it already created
     * @throws Exception
     */
    private FolderData createFolder(String path) throws Exception {
        //create inexistent elements from configured paths as admin
        List<String> pathElements = getPathElements(path);
        FolderData parentFolder = fileFolderService.getFolder(FILEPLAN_CONTEXT, RECORD_CONTAINER_PATH);
        // for(String pathElement: pathElements)
        int pathElementsLength = pathElements.size();
        for (int i = 0; i < pathElementsLength; i++) {
            String pathElement = pathElements.get(i);
            FolderData folder = fileFolderService.getFolder(RECORD_CATEGORY_CONTEXT,
                    parentFolder.getPath() + "/" + pathElement);
            if (folder != null) {
                parentFolder = folder;
            } else {
                if (i == 0) {
                    //create root category
                    parentFolder = createRootRecordCategoryWithFixedName(parentFolder, pathElement);
                } else if (pathElementsLength > 1 && i == (pathElementsLength - 1)) {
                    //create record folder
                    parentFolder = createRecordFolderWithFixedName(parentFolder, pathElement);
                } else {
                    //create child category
                    parentFolder = createRecordCategoryWithFixedName(parentFolder, pathElement);
                }
            }
        }
        return parentFolder;
    }

    /**
     * Helper method for preparing events for loading records randomly in the record folders structure or in specified record folder paths.
     *
     * @param loaderSessionsToCreate - the number of still active loader sessions
     * @param nextEvents - list of prepared events
     */
    private void prepareRecords(int loaderSessionsToCreate, List<Event> nextEvents) {
        calculateListOfEmptyFolders();
        List<FolderData> emptyFolders = new ArrayList<FolderData>();
        emptyFolders.addAll(mapOfRecordsPerRecordFolder.keySet());
        while (nextEvents.size() < loaderSessionsToCreate) {
            if (mapOfRecordsPerRecordFolder == null || mapOfRecordsPerRecordFolder.size() == 0) {
                break;
            }
            // Schedule a load for each folder
            for (FolderData emptyFolder : emptyFolders) {
                int recordsToCreate = mapOfRecordsPerRecordFolder.get(emptyFolder)
                        - (int) emptyFolder.getFileCount();
                if (recordsToCreate <= 0) {
                    mapOfRecordsPerRecordFolder.remove(emptyFolder);
                } else {
                    try {
                        // Create a lock folder that has too many files and folders so that it won't be picked up
                        // by this process in subsequent trawls
                        String lockPath = emptyFolder.getPath() + "/locked";
                        FolderData lockFolder = new FolderData(UUID.randomUUID().toString(),
                                emptyFolder.getContext(), lockPath, Long.MAX_VALUE, Long.MAX_VALUE);
                        fileFolderService.createNewFolder(lockFolder);
                        // We locked this, so the load can be scheduled.
                        // The loader will remove the lock when it completes
                        DBObject loadData = BasicDBObjectBuilder.start()
                                .add(FIELD_CONTEXT, emptyFolder.getContext()).add(FIELD_PATH, emptyFolder.getPath())
                                .add(FIELD_RECORDS_TO_CREATE, Integer.valueOf(recordsToCreate)).get();
                        Event loadEvent = new Event(eventNameLoadRecords, loadData);
                        // Each load event must be associated with a session
                        String sessionId = sessionService.startSession(loadData);
                        loadEvent.setSessionId(sessionId);
                        // Add the event to the list
                        nextEvents.add(loadEvent);
                        mapOfRecordsPerRecordFolder.remove(emptyFolder);
                    } catch (Exception e) {
                        mapOfRecordsPerRecordFolder.remove(emptyFolder);
                        // The lock was already applied; find another
                        continue;
                    }
                }
                // Check if we have enough
                if (nextEvents.size() >= loaderSessionsToCreate) {
                    break;
                }
            }
        }
    }
}