/* * Copyright (C) 2005-2010 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 . */ package org.alfresco.filesys.repo.rules; import java.util.ArrayList; import java.util.Date; import org.alfresco.filesys.repo.ResultCallback; import org.alfresco.filesys.repo.rules.commands.CloseFileCommand; import org.alfresco.filesys.repo.rules.commands.CompoundCommand; import org.alfresco.filesys.repo.rules.commands.CopyContentCommand; import org.alfresco.filesys.repo.rules.commands.DeleteFileCommand; import org.alfresco.filesys.repo.rules.commands.RenameFileCommand; import org.alfresco.filesys.repo.rules.commands.RestoreFileCommand; import org.alfresco.filesys.repo.rules.operations.CloseFileOperation; import org.alfresco.filesys.repo.rules.operations.DeleteFileOperation; import org.alfresco.filesys.repo.rules.operations.MoveFileOperation; import org.alfresco.filesys.repo.rules.operations.RenameFileOperation; import org.alfresco.jlan.server.filesys.FileName; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.service.cmr.repository.NodeRef; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * This is an instance of a rename, delete, move scenario triggered by a rename of a * file matching a specified pattern. *

* a) Original file is renamed. Typically with an obscure name. * b) Renamed file is deleted via delete command or via deleteOnClose flag and close operation. * c) Temp file is moved into original file location. * *

* If this filter is active then this is what happens. * a) Original file is renamed: * - File is renamed. * b) Renamed file is deleted via delete command or via deleteOnClose flag and close operation: * - File is deleted. * c) Temp file is moved into original file location - Scenario fires * - Deleted file is restored. * - Restored file is renamed to it's original name. * - Content from file that must be moved is copied to restored file. * - File that must be moved is deleted. */ public class ScenarioRenameDeleteMoveInstance implements ScenarioInstance { private static Log logger = LogFactory.getLog(ScenarioRenameDeleteMoveInstance.class); enum InternalState { NONE, DELETE, MOVE } InternalState internalState = InternalState.NONE; private Date startTime = new Date(); private String fileMiddle; private String fileFrom; private String fileEnd; private Ranking ranking; private boolean deleteBackup; /** * Timeout in ms. Default 30 seconds. */ private long timeout = 30000; private boolean isComplete; private String folderMiddle; private String folderEnd; private NodeRef originalNodeRef; /** * Evaluate the next operation * * @param operation */ public Command evaluate(Operation operation) { /** * Anti-pattern : timeout */ Date now = new Date(); if (now.getTime() > startTime.getTime() + getTimeout()) { if (logger.isDebugEnabled()) { logger.debug("Instance timed out"); } isComplete = true; return null; } switch (internalState) { case NONE: if (operation instanceof RenameFileOperation) { RenameFileOperation r = (RenameFileOperation) operation; fileMiddle = r.getFrom(); fileEnd = r.getTo(); String[] paths = FileName.splitPath(r.getFromPath()); folderMiddle = paths[0]; String[] paths2 = FileName.splitPath(r.getToPath()); folderEnd = paths2[0]; internalState = InternalState.DELETE; } else { // anything else bomb out if (logger.isDebugEnabled()) { logger.debug("State error, expected a RENAME"); } isComplete = true; } case DELETE: if (operation instanceof DeleteFileOperation) { internalState = InternalState.MOVE; DeleteFileOperation d = (DeleteFileOperation) operation; if (d.getName().equalsIgnoreCase(fileEnd)) { ArrayList commands = new ArrayList(); ArrayList postCommitCommands = new ArrayList(); ArrayList postErrorCommands = new ArrayList(); // Rename node to remove "hidden". In this case node will be moved to the archive store and can be restored later. // This can be replaced with command that removes hidden aspect in future(when ContentDiskDriver2.setFileInformation() method will support hidden attribute) RenameFileCommand r1 = new RenameFileCommand(fileEnd, "tmp" + fileEnd, d.getRootNodeRef(), folderEnd + "\\" + fileEnd, folderEnd + "\\" + "tmp" + fileEnd); fileEnd = "tmp" + fileEnd; commands.add(r1); commands.add(new DeleteFileCommand(fileEnd, d.getRootNodeRef(), folderEnd + "\\" + fileEnd)); postCommitCommands.add(newDeleteFileCallbackCommand()); return new CompoundCommand(commands, postCommitCommands, postErrorCommands); } } if (operation instanceof CloseFileOperation) { CloseFileOperation c = (CloseFileOperation) operation; if (c.getNetworkFile().hasDeleteOnClose() && c.getName().equalsIgnoreCase(fileEnd)) { internalState = InternalState.MOVE; ArrayList commands = new ArrayList(); ArrayList postCommitCommands = new ArrayList(); ArrayList postErrorCommands = new ArrayList(); // Rename node to remove "hidden". In this case node will be moved to the archive store and can be restored later. RenameFileCommand r1 = new RenameFileCommand(fileEnd, "tmp" + fileEnd, c.getRootNodeRef(), folderEnd + "\\" + fileEnd, folderEnd + "\\" + "tmp" + fileEnd); fileEnd = "tmp" + fileEnd; commands.add(r1); commands.add(new CloseFileCommand(fileEnd, c.getNetworkFile(), c.getRootNodeRef(), folderEnd + "\\" + fileEnd)); postCommitCommands.add(newDeleteFileCallbackCommand()); return new CompoundCommand(commands, postCommitCommands, postErrorCommands); } } break; case MOVE: if (operation instanceof MoveFileOperation && originalNodeRef != null) { if (logger.isDebugEnabled()) { logger.info("Tracking rename: " + operation); } MoveFileOperation m = (MoveFileOperation) operation; if (fileMiddle.equalsIgnoreCase(m.getTo())) { if (logger.isDebugEnabled()) { logger.debug("Got second rename"); } fileFrom = m.getFrom(); String[] paths = FileName.splitPath(m.getFromPath()); String oldFolder = paths[0]; ArrayList commands = new ArrayList(); RestoreFileCommand rest1 = new RestoreFileCommand(fileEnd, m.getRootNodeRef(), folderEnd, 0, originalNodeRef); RenameFileCommand r1 = new RenameFileCommand(fileEnd, fileMiddle, m.getRootNodeRef(), folderEnd + "\\" + fileEnd, folderMiddle + "\\" + fileMiddle); commands.add(rest1); commands.add(r1); CopyContentCommand copyContent = new CopyContentCommand(fileFrom, fileMiddle, m.getRootNodeRef(), oldFolder + "\\" + fileFrom, folderMiddle + "\\" + fileMiddle); commands.add(copyContent); DeleteFileCommand d1 = new DeleteFileCommand(oldFolder, m.getRootNodeRef(), oldFolder + "\\" + fileFrom); commands.add(d1); isComplete = true; return new CompoundCommand(commands); } } break; } return null; } @Override public boolean isComplete() { return isComplete; } @Override public Ranking getRanking() { return ranking; } public void setRanking(Ranking ranking) { this.ranking = ranking; } public String toString() { return "ScenarioRenameDeleteMove:" + fileMiddle; } public void setTimeout(long timeout) { this.timeout = timeout; } public long getTimeout() { return timeout; } public void setDeleteBackup(boolean deleteBackup) { this.deleteBackup = deleteBackup; } public boolean isDeleteBackup() { return deleteBackup; } /** * Called for delete file. */ private ResultCallback newDeleteFileCallbackCommand() { return new ResultCallback() { @Override public void execute(Object result) { if (result instanceof NodeRef) { logger.debug("got node ref of deleted node"); originalNodeRef = (NodeRef) result; } } @Override public TxnReadState getTransactionRequired() { return TxnReadState.TXN_NONE; } }; } }