/* * Copyright 2012-2013 eBay Software Foundation and selendroid committers * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package io.selendroid.standalone.server.util; import io.selendroid.standalone.SelendroidConfiguration; import io.selendroid.standalone.exceptions.AndroidSdkException; import io.selendroid.standalone.server.model.SelendroidStandaloneDriver; import java.io.File; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.logging.Logger; public class FolderMonitor implements Runnable { private static final Logger log = Logger.getLogger(FolderMonitor.class.getName()); private SelendroidConfiguration selendroidConfiguration; private SelendroidStandaloneDriver selendroidStandaloneDriver; private WatchService folderWatcher; private final Object stoppedLock; private boolean stopped; private Thread thread; public FolderMonitor(SelendroidStandaloneDriver selendroidStandaloneDriver, SelendroidConfiguration selendroidConfiguration) throws IOException { this.selendroidStandaloneDriver = selendroidStandaloneDriver; this.selendroidConfiguration = selendroidConfiguration; stoppedLock = new Object(); stopped = false; init(); folderWatcher = FileSystems.getDefault().newWatchService(); Path watchedFolder = Paths.get(selendroidConfiguration.getAppFolderToMonitor()); try { watchedFolder.register(folderWatcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE); } catch (NoSuchFileException e) { stop(); log.warning("invalid location: " + new File(selendroidConfiguration.getAppFolderToMonitor()) .getAbsolutePath()); } } private void init() { File[] listOfFiles = new File(selendroidConfiguration.getAppFolderToMonitor()).listFiles(); if (listOfFiles == null) { return; } for (File file : listOfFiles) { if (isResigned(file)) { file.delete(); } else if (isApp(file)) { addApplication(file); } } } @Override public void run() { synchronized (stoppedLock) { while (!stopped) { checkForChanges(); try { stoppedLock.wait(1000, 0); } catch (InterruptedException ignore) { } } } } private void checkForChanges() { final WatchKey key = folderWatcher.poll(); if (key != null) { for (WatchEvent<?> watchEvent : key.pollEvents()) { final Path filePath = (Path) watchEvent.context(); final WatchEvent.Kind<?> kind = watchEvent.kind(); log.fine(kind + " : " + filePath); handleFileChange(kind, new File(selendroidConfiguration.getAppFolderToMonitor(), filePath.getFileName().toString())); } boolean valid = key.reset(); if (!valid) { log.warning("Cannot monitor this folder anymore. Has it been deleted?"); stop(); } } } private void handleFileChange(WatchEvent.Kind kind, File file) { if (kind.equals(StandardWatchEventKinds.ENTRY_CREATE)) { if (isApp(file)) { // new file but only added to the list only // if they are not a resigned files. if(!isResigned(file)) { log.info("New app found! " + file.getName()); addToAppStore(file); } } } else if (kind.equals(StandardWatchEventKinds.ENTRY_MODIFY)) { log.info("App modified - no handler implemented!"); } else if (kind.equals(StandardWatchEventKinds.ENTRY_DELETE)) { log.info("App deleted - no handler implemented!"); } } private void addApplication(File file) { String app; if (isApp(file)) { if (!isResigned(file)) { app = file.getAbsolutePath(); selendroidConfiguration.addSupportedApp(app); log.info("File added to supported list:\n\t" + app); } } } private void addToAppStore(File file) { String app; if (isApp(file)) { app = file.getAbsolutePath(); try { selendroidStandaloneDriver.addToAppsStore(file); log.info("File added to app store:\n\t" + app); }catch (AndroidSdkException e) { log.info("An error occurred while accessing the details of'" + file.getName() + "'. "); } } } private boolean isApp(File file) { if (file != null) { return file.getAbsolutePath().endsWith(".apk"); } return false; } private boolean isResigned(File file) { return (isApp(file) && file.getAbsolutePath().contains("resigned")); } public void start() { thread = new Thread(this); thread.start(); log.info("The Folder Monitor has been started with '" + selendroidConfiguration.getAppFolderToMonitor() + "'. New apps in this folder will be avalilable for testing immediately."); } public void stop() { synchronized (stoppedLock) { stopped = true; } try { if (thread != null) { thread.join(); thread = null; } } catch (InterruptedException ignore) { } } }