/* * Syncany, www.syncany.org * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.syncany.operations; import java.awt.MouseInfo; import java.awt.Point; import java.awt.Robot; import java.io.File; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import org.syncany.config.UserConfig; import org.syncany.operations.cleanup.CleanupOperation; import org.syncany.plugins.transfer.StorageException; import org.syncany.plugins.transfer.TransferManager; import org.syncany.plugins.transfer.files.ActionRemoteFile; /** * The action handler manages the {@link ActionRemoteFile}s written during an {@link Operation}. * * <p>In particular, it uploads an initial action file when the operation is started, deletes it * when it is finished/terminated, and renews the operation's action file in a given interval. * * <p>The renewal is necessary to show other clients that the operation is still running. To ensure * action file renewal, the {@link #start()} method starts a timer that uploads a new {@link ActionRemoteFile} * every {@link #ACTION_RENEWAL_INTERVAL} milliseconds. The {@link #finish()} method stops this timer. * * @see CleanupOperation * @author Philipp C. Heckel <philipp.heckel@gmail.com> */ public class ActionFileHandler { private static final Logger logger = Logger.getLogger(ActionFileHandler.class.getSimpleName()); /** * Defines the time that the action files updated while an operation is running. * * This time period must be (significantly) smaller than the ignore time defined in * {@link CleanupOperation#ACTION_FILE_DELETE_TIME}. */ public static final int ACTION_RENEWAL_INTERVAL = 2*60*1000; // Minutes private TransferManager transferManager; private ActionRemoteFile actionFile; private Timer actionRenewalTimer; public ActionFileHandler(TransferManager transferManager, String operationName, String machineName) { try { this.transferManager = transferManager; this.actionFile = new ActionRemoteFile(operationName, machineName, System.currentTimeMillis()); this.actionRenewalTimer = createNewActionRenewalTimer(); } catch (Exception e) { throw new RuntimeException(e); } } private Timer createNewActionRenewalTimer() { return new Timer("ActRenewTim"); } public void start() throws Exception { logger.log(Level.INFO, "Starting action for " + actionFile + " ..."); uploadActionFile(actionFile); scheduleActionRenewalTask(); } public void finish() throws StorageException { logger.log(Level.INFO, "Finishing action for " + actionFile + " ..."); cancelActionRenewalTask(); deleteActionFile(actionFile); } private void deleteActionFile(ActionRemoteFile actionFile) throws StorageException { logger.log(Level.INFO, "Deleting action file: " + actionFile); transferManager.delete(actionFile); } private void uploadActionFile(ActionRemoteFile actionFile) throws Exception { logger.log(Level.INFO, "Uploading action file: " + actionFile); File tempActionFile = File.createTempFile("syncany-action-", ".tmp"); tempActionFile.deleteOnExit(); transferManager.upload(tempActionFile, actionFile); tempActionFile.delete(); } private void scheduleActionRenewalTask() { logger.log(Level.INFO, "Scheduling action renewal task for every " + (ACTION_RENEWAL_INTERVAL/60/1000) + " minutes, for " + actionFile + " ..."); actionRenewalTimer.schedule(new TimerTask() { @Override public void run() { renewActionFile(); if (UserConfig.isPreventStandby()) { preventStandby(); } } }, ACTION_RENEWAL_INTERVAL, ACTION_RENEWAL_INTERVAL); } private void cancelActionRenewalTask() { actionRenewalTimer.cancel(); actionRenewalTimer = createNewActionRenewalTimer(); } private synchronized void renewActionFile() { try { logger.log(Level.INFO, "Scheduling action renewal task for every " + (ACTION_RENEWAL_INTERVAL/60/1000) + " minutes, for " + actionFile + " ..."); ActionRemoteFile oldActionFile = actionFile; ActionRemoteFile newActionFile = new ActionRemoteFile(oldActionFile.getOperationName(), oldActionFile.getClientName(), System.currentTimeMillis()); uploadActionFile(newActionFile); deleteActionFile(oldActionFile); actionFile = newActionFile; } catch (Exception e) { logger.log(Level.SEVERE, "ERROR: Cannot renew action file!", e); } } private void preventStandby() { try { Robot robot = new Robot(); Point currentMousePosition = MouseInfo.getPointerInfo().getLocation(); Point tempMousePosition = (currentMousePosition.x > 0) ? new Point(currentMousePosition.x - 10, currentMousePosition.y) : new Point( currentMousePosition.x + 10, currentMousePosition.y); logger.log(Level.INFO, "Standby prevention: Moving mouse 1px (and back): " + currentMousePosition); robot.mouseMove(tempMousePosition.x, tempMousePosition.y); robot.mouseMove(currentMousePosition.x, currentMousePosition.y); } catch (Exception e) { if (e.getMessage() != null && e.getMessage().contains("headless")) { logger.log(Level.INFO, "Cannot prevent standby, because headless mode is enabled (no GUI environment)"); } else { logger.log(Level.WARNING, "Standby prevention failed (headless mode?).", e); } } } }