/* * Copyright (C) 2005-2014 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.unfiled; import java.util.ArrayList; import java.util.List; import java.util.UUID; 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; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBObject; /** * Prepare event for unfiled record folders structure * * @author Silviu Dinuta * @since 1.0 * */ public class ScheduleUnfiledRecordFolderLoaders extends RmBaseEventProcessor { public static final String EVENT_NAME_LOAD_UNFILED_RECORD_FOLDERS = "loadUnfiledRecordFolders"; public static final String EVENT_NAME_SCHEDULE_LOADERS = "scheduleUnfiledFoldersLoaders"; public static final String EVENT_NAME_LOADING_COMPLETE = "loadingUnfiledRecordFoldersComplete"; @Autowired private SessionService sessionService; private int maxActiveLoaders; private int unfiledRecordFolderDepth; private int unfiledRecordFolderNumber; private boolean createUnfiledRecordFolderStructure; private int rootUnfiledRecordFolderNumber; private long loadCheckDelay; private String username; private int maxLevel; private String eventNameLoadUnfiledRecordFolders = EVENT_NAME_LOAD_UNFILED_RECORD_FOLDERS; private String eventNameScheduleLoaders = EVENT_NAME_SCHEDULE_LOADERS; private String eventNameLoadingComplete = EVENT_NAME_LOADING_COMPLETE; public int getMaxActiveLoaders() { return maxActiveLoaders; } public void setMaxActiveLoaders(int maxActiveLoaders) { this.maxActiveLoaders = maxActiveLoaders; } public int getUnfiledRecordFolderDepth() { return unfiledRecordFolderDepth; } public void setUnfiledRecordFolderDepth(int unfiledRecordFolderDepth) { this.unfiledRecordFolderDepth = unfiledRecordFolderDepth; this.maxLevel = this.unfiledRecordFolderDepth + 4; // Add levels for "/Sites<L1>/siteId<L2>/documentLibrary<L3>/Unfiled Records<L4>" } public int getMaxLevel() { return maxLevel; } public int getUnfiledRecordFolderNumber() { return unfiledRecordFolderNumber; } public void setUnfiledRecordFolderNumber(int unfiledRecordFolderNumber) { this.unfiledRecordFolderNumber = unfiledRecordFolderNumber; } public boolean isCreateUnfiledRecordFolderStructure() { return createUnfiledRecordFolderStructure; } public void setCreateUnfiledRecordFolderStructure(boolean createUnfiledRecordFolderStructure) { this.createUnfiledRecordFolderStructure = createUnfiledRecordFolderStructure; } public int getRootUnfiledRecordFolderNumber() { return rootUnfiledRecordFolderNumber; } public void setRootUnfiledRecordFolderNumber(int rootUnfiledRecordFolderNumber) { this.rootUnfiledRecordFolderNumber = rootUnfiledRecordFolderNumber; } public long getLoadCheckDelay() { return loadCheckDelay; } public void setLoadCheckDelay(long loadCheckDelay) { this.loadCheckDelay = loadCheckDelay; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEventNameLoadUnfiledRecordFolders() { return eventNameLoadUnfiledRecordFolders; } public String getEventNameScheduleLoaders() { return eventNameScheduleLoaders; } public String getEventNameLoadingComplete() { return eventNameLoadingComplete; } /** * Override the {@link #EVENT_NAME_LOAD_UNFILED_RECORD_FOLDERS default} output event name */ public void setEventNameLoadUnfiledRecordFolders(String eventNameLoadUnfiledRecordFolders) { this.eventNameLoadUnfiledRecordFolders = eventNameLoadUnfiledRecordFolders; } /** * Override the {@link #EVENT_NAME_SCHEDULE_LOADERS default} output event name */ public void setEventNameScheduleLoaders(String eventNameScheduleLoaders) { this.eventNameScheduleLoaders = eventNameScheduleLoaders; } /** * Override the {@link #EVENT_NAME_LOADING_COMPLETE default} output event name */ public void setEventNameLoadingComplete(String eventNameLoadingComplete) { this.eventNameLoadingComplete = eventNameLoadingComplete; } @Override protected EventResult processEvent(Event arg0) 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 (!createUnfiledRecordFolderStructure) { return new EventResult("Unfiled Record Folders structure creation not wanted.", false); } if(unfiledRecordFolderDepth > 0) { //Load root unfiled record folders prepareRootUnfiledRecordFolders(loaderSessionsToCreate, nextEvents); } if(unfiledRecordFolderDepth > 1) { //Load unfiled record folder children prepareUnfiledRecordFolders(loaderSessionsToCreate, nextEvents); } // If there are no events, then we have finished String msg = null; if (loaderSessionsToCreate > 0 && nextEvents.size() == 0) { // There are no files or folders to load even though there are sessions available Event nextEvent = new Event(eventNameLoadingComplete, null); nextEvents.add(nextEvent); msg = "Loading completed. Raising 'done' event."; } else { // Reschedule self Event nextEvent = new Event(eventNameScheduleLoaders, 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; } private void prepareRootUnfiledRecordFolders(int loaderSessionsToCreate, List<Event> nextEvents) { int skip = 0; int limit = 100; while (nextEvents.size() < loaderSessionsToCreate) { // Get categories needing loading List<FolderData> emptyFolders = fileFolderService.getFoldersByCounts( UNFILED_CONTEXT, Long.valueOf(UNFILED_RECORD_CONTAINER_LEVEL), Long.valueOf(UNFILED_RECORD_CONTAINER_LEVEL),//min and max level are 4, level of unfiled record container 0L, Long.valueOf((rootUnfiledRecordFolderNumber - 1)),//limit the maximum number of child folders to rootUnfiledRecordFolderNumber - 1 null, null, // Ignore file limits skip, limit); skip += limit; if (emptyFolders.size() == 0) { // The folders were populated in the mean time break; } // Schedule a load for each folder for (FolderData emptyFolder : emptyFolders) { int unfiledRecordFolderToCreate = rootUnfiledRecordFolderNumber - (int)emptyFolder.getFolderCount(); 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_UNFILED_ROOT_FOLDERS_TO_CREATE, Integer.valueOf(unfiledRecordFolderToCreate)) .add(FIELD_UNFILED_FOLDERS_TO_CREATE, Integer.valueOf(0)) .add(FIELD_SITE_MANAGER, username) .get(); Event loadEvent = new Event(eventNameLoadUnfiledRecordFolders, 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); } catch (Exception e) { // The lock was already applied; find another continue; } // Check if we have enough if (nextEvents.size() >= loaderSessionsToCreate) { break; } } } } private void prepareUnfiledRecordFolders(int loaderSessionsToCreate, List<Event> nextEvents) { int skip = 0; int limit = 100; while (nextEvents.size() < loaderSessionsToCreate) { // Get folders needing loading List<FolderData> emptyFolders = fileFolderService.getFoldersByCounts( UNFILED_CONTEXT, Long.valueOf(UNFILED_RECORD_CONTAINER_LEVEL+1),//min level is 5, level of root unfiled record folders Long.valueOf(maxLevel-1),//max level is 4+unfiledRecordFolderDepth-1 0L, Long.valueOf(unfiledRecordFolderNumber - 1),//limit the maximum number of child folders to rootUnfiledRecordFolderNumber - 1 null, null, // Ignore file limits skip, limit); skip += limit; if (emptyFolders.size() == 0) { // The folders were populated in the mean time break; } // Schedule a load for each folder for (FolderData emptyFolder : emptyFolders) { int foldersToCreate = unfiledRecordFolderNumber - (int) emptyFolder.getFolderCount(); 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_UNFILED_ROOT_FOLDERS_TO_CREATE, Integer.valueOf(0)) .add(FIELD_UNFILED_FOLDERS_TO_CREATE, Integer.valueOf(foldersToCreate)) .add(FIELD_SITE_MANAGER, username) .get(); Event loadEvent = new Event(eventNameLoadUnfiledRecordFolders, 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); } catch (Exception e) { // The lock was already applied; find another continue; } // Check if we have enough if (nextEvents.size() >= loaderSessionsToCreate) { break; } } } } }