/*
* Copyright 2013-2014 Odysseus Software GmbH
*
* 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 org.musicmount.live;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.musicmount.io.file.FileResource;
import org.musicmount.util.FolderTreeWatcher;
import org.musicmount.util.ProgressHandler;
public class LiveMountUpdater {
protected static final Logger LOGGER = Logger.getLogger(LiveMountUpdater.class.getName());
/*
* Timer task performing the updates.
* An update is triggered if
* (a) something changed since the last update and
* (b) there was no change since delayMillis
*/
class UpdateTask extends TimerTask {
private final ExecutorService executor;
private final Runnable command;
private Future<?> future;
private long updateChange = lastChange;
UpdateTask(final FileResource musicFolder, final String musicPath, final LiveMountBuilder builder, final LiveMountServlet servlet) {
this.executor = Executors.newSingleThreadExecutor();
this.command = new Runnable() {
@Override
public void run() {
LOGGER.info("Updating live mount...");
try {
servlet.setMount(builder.update(musicFolder, musicPath, ProgressHandler.NOOP));
LOGGER.info("Done.");
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Could not update live mount", e);
}
}
};
}
@Override
public void run() {
if (future != null) {
if (future.isDone()) {
future = null;
} else { // busy
return;
}
}
final long lastChange = getLastChange();
if (lastChange > updateChange && System.currentTimeMillis() - lastChange >= getDelayMillis()) {
future = executor.submit(command);
updateChange = lastChange;
}
}
@Override
public boolean cancel() {
executor.shutdownNow();
return super.cancel();
}
}
/*
* minimum period of time without further changes required to trigger an update after a change.
*/
private final long delayMillis;
private long lastChange = 0;
private Timer updateTimer;
private Thread watcherThread;
public LiveMountUpdater(long delayMillis) {
this.delayMillis = delayMillis;
}
public void start(FileResource musicFolder, String musicPath, LiveMountBuilder builder, LiveMountServlet servlet) throws IOException {
watcherThread = new Thread(new FolderTreeWatcher(musicFolder.getPath(), new FolderTreeWatcher.Delegate() {
@Override
public void pathModified(Path path) {
lastChange = System.currentTimeMillis();
}
@Override
public void pathDeleted(Path path) {
lastChange = System.currentTimeMillis();
}
@Override
public void pathAdded(Path path) {
lastChange = System.currentTimeMillis();
}
}));
watcherThread.start();
updateTimer = new Timer(true);
long periodMillis = Math.max(1000L, delayMillis / 10); // at most once a second
updateTimer.schedule(new UpdateTask(musicFolder, musicPath, builder, servlet), periodMillis, periodMillis);
}
long getLastChange() {
return lastChange;
}
long getDelayMillis() {
return delayMillis;
}
public boolean isStarted() {
return watcherThread != null && watcherThread.isAlive();
}
public void stop() {
if (updateTimer != null) {
updateTimer.cancel();
updateTimer = null;
}
if (watcherThread != null) {
watcherThread.interrupt();
watcherThread = null;
}
}
}