package bndtools.launch;
import java.io.Closeable;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import org.bndtools.build.api.BuildListener;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import aQute.bnd.osgi.Jar;
import aQute.bnd.service.RepositoryListenerPlugin;
import aQute.bnd.service.RepositoryPlugin;
/**
* This class watches the build and allows you to schedule an update some time after the last build operation ended.
*/
public abstract class UpdateGuard implements Closeable {
private static final int GRACE_PERIOD = 500;
private final static Timer timer = new Timer(true);
private final BundleContext context;
private final AtomicBoolean closed = new AtomicBoolean(true);
private ServiceRegistration<BuildListener> buildListener;
private ServiceRegistration<RepositoryListenerPlugin> repositoryListener;
private TimerTask trigger;
public UpdateGuard(BundleContext context) {
this.context = context;
}
public void open() {
if (!closed.getAndSet(false))
throw new IllegalStateException("Already opened");
//
// We wait for build changes. We never update during a build
// and we will wait a bit after a build ends.
//
buildListener = context.registerService(BuildListener.class, new BuildListener() {
@Override
public void buildStarting(IProject project) {
off();
}
@Override
public void builtBundles(IProject project, IPath[] paths) {}
@Override
public void released(IProject project) {
on();
}
}, null);
//
// We also wait for repository changes, though they will generally cause a rebuild as well.
//
repositoryListener = context.registerService(RepositoryListenerPlugin.class, new RepositoryListenerPlugin() {
@Override
public void repositoryRefreshed(RepositoryPlugin repository) {
on();
}
@Override
public void repositoriesRefreshed() {
on();
}
@Override
public void bundleRemoved(RepositoryPlugin repository, Jar jar, File file) {
on();
}
@Override
public void bundleAdded(RepositoryPlugin repository, Jar jar, File file) {
on();
}
}, null);
}
private void off() {
if (closed.get())
return;
synchronized (timer) {
if (trigger != null)
trigger.cancel();
trigger = null;
}
}
private void on() {
if (closed.get())
return;
synchronized (timer) {
if (trigger != null)
trigger.cancel();
trigger = new TimerTask() {
@Override
public void run() {
if (closed.get())
return;
update();
}
};
timer.schedule(trigger, GRACE_PERIOD);
}
}
protected abstract void update();
public void kick() {
on();
}
@Override
public void close() {
if (closed.getAndSet(true))
return;
try {
buildListener.unregister();
} catch (Exception e) {
// ignore
}
try {
repositoryListener.unregister();
} catch (Exception e) {
// ignore
}
}
}