bndtools.central.sync.SynchronizeWorkspaceWithEclipse Maven / Gradle / Ivy
package bndtools.central.sync;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import aQute.bnd.build.Project;
import aQute.bnd.build.Workspace;
import aQute.bnd.build.api.OnWorkspace;
import aQute.bnd.osgi.Processor;
import aQute.lib.concurrent.serial.TriggerRepeat;
import aQute.lib.io.IO;
import aQute.lib.watcher.FileWatcher;
import bndtools.central.Central;
/**
* Synchronizes changes in the bnd workspace with Eclipse. This will use a Java
* Watcher & checks the modification date since on MacOS the Java Watcher does
* not work well.
*
* If a difference is detected a 1sec job is started that compares the workspace
* with the file system. Any deltas are processed by creating or deleting the
* project.
*/
@Component(enabled = false)
public class SynchronizeWorkspaceWithEclipse {
static IWorkspace eclipse = ResourcesPlugin.getWorkspace();
final static IWorkspaceRoot root = eclipse.getRoot();
// This restricted API warning can be ignored because the constant
// is resolved at compile time.
@SuppressWarnings("restriction")
final static String BNDTOOLS_NATURE = bndtools.Plugin.BNDTOOLS_NATURE;
final TriggerRepeat lock = new TriggerRepeat();
ScheduledFuture> schedule;
long lastModified = -1;
FileWatcher watcher;
File dir;
boolean macos;
OnWorkspace event;
@Reference
Workspace workspace;
@Activate
void activate() throws IOException {
schedule = Processor.getScheduledExecutor()
.scheduleAtFixedRate(this::check, 1000, 300, TimeUnit.MILLISECONDS);
macos = "MacOSX".equalsIgnoreCase(osname());
event = workspace.on("workspace sync")
.projects(this::sync);
}
@Deactivate
void deactivate() throws IOException {
schedule.cancel(true);
if (watcher != null)
IO.close(watcher);
IO.close(event);
}
private void sync(Collection doNotUse) {
if (!lock.trigger()) {
// already being handled
return;
}
Job sync = Job.create("sync workspace", (IProgressMonitor monitor) -> {
Map projects = Stream.of(root.getProjects())
.collect(Collectors.toMap(IProject::getName, p -> p));
SubMonitor subMonitor = SubMonitor.convert(monitor, 5);
do {
try {
// Reset this on every iteration (divides the remaining
// time in the monitor into 5, however much is remaining).
subMonitor.setWorkRemaining(5);
subMonitor.setTaskName("Waiting for changes to finish");
// Step 1: wait for catch-up
subMonitor.split(1);
// catch up with some changes
Thread.sleep(500);
List createProjects = new ArrayList<>(100);
List removeProjects = new ArrayList<>(100);
workspace.writeLocked(() -> {
Collection allProjects = workspace.getAllProjects();
// Step 2: look for projects to add/update
SubMonitor loopMonitor = subMonitor.split(1)
.setWorkRemaining(allProjects.size());
subMonitor.setTaskName("Scanning bnd workspace for projects to add/refresh");
for (Project model : allProjects) {
loopMonitor.split(1);
loopMonitor.subTask("Checking project " + model.getName());
IProject project = projects.remove(model.getName());
if (project == null) {
createProjects.add(model);
}
}
// Step 3: look for projects to remove.
loopMonitor = subMonitor.split(1)
.setWorkRemaining(projects.size());
subMonitor.setTaskName("Scanning Eclipse workspace for projects to remove");
for (IProject project : projects.values()) {
loopMonitor.split(1);
loopMonitor.subTask("Checking project " + project.getName());
if (!project.isAccessible())
continue;
if (!project.hasNature(BNDTOOLS_NATURE))
continue;
removeProjects.add(project);
}
return null;
}, subMonitor::isCanceled);
// Step 4: add/refresh projects that were found
SubMonitor loopMonitor = subMonitor.split(1)
.setWorkRemaining(createProjects.size());
subMonitor.setTaskName("Adding/creating bnd projects in Eclipse workspace");
for (Project create : createProjects) {
loopMonitor.subTask("Adding/creating project " + create.getName());
IProject p = WorkspaceSynchronizer.createProject(create.getBase(), create,
loopMonitor.split(1));
}
// Step 5: remove projects that were not found
loopMonitor = subMonitor.split(1)
.setWorkRemaining(removeProjects.size());
subMonitor.setTaskName("Removing projects that are not part of the bnd workspace");
for (IProject remove : removeProjects) {
loopMonitor.subTask("Removing project " + remove.getName());
WorkspaceSynchronizer.removeProject(remove, loopMonitor.split(1));
}
} catch (OperationCanceledException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
} while (lock.doit());
});
sync.setRule(root);
sync.setPriority(Job.SHORT);
sync.schedule();
}
private void watcher(File directory) {
if (watcher != null)
IO.close(watcher);
try {
watcher = new FileWatcher.Builder().executor(Processor.getExecutor())
.file(directory)
.changed(this::changed)
.build();
} catch (IOException e) {
watcher = null;
About.logger.error("could not create watcher for directory {}", directory);
}
}
private void changed(File file1, String kind) {
workspace.forceRefreshProjects();
}
private String osname() {
return System.getProperty("os.name", "?")
.replaceAll("[\\s-_]", "");
}
private void check() {
Workspace ws = Central.getWorkspaceIfPresent();
if (ws == null)
return;
if (!ws.getBase()
.equals(dir)) {
dir = ws.getBase();
watcher(dir);
}
long lastModified2 = ws.getBase()
.lastModified();
if (lastModified < lastModified2) {
lastModified = lastModified2;
long diff = System.currentTimeMillis() - lastModified2;
workspace.forceRefreshProjects();
}
}
}