All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bndtools.central.RepositoriesViewRefresher Maven / Gradle / Ivy

The newest version!
package bndtools.central;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;

import org.bndtools.utils.swt.SWTConcurrencyUtil;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Display;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;

import aQute.bnd.build.Workspace;
import aQute.bnd.build.WorkspaceRepository;
import aQute.bnd.osgi.Jar;
import aQute.bnd.service.RepositoryListenerPlugin;
import aQute.bnd.service.RepositoryPlugin;
import bndtools.Plugin;

public class RepositoriesViewRefresher implements RepositoryListenerPlugin {

	public interface RefreshModel {
		List getRepositories();
	}

	// private static final ILogger logger =
	// Logger.getLogger(RepositoriesViewRefresher.class);

	enum State {
		IDLE,
		BUSY,
		REDO;
	}

	private final AtomicReference						state	= new AtomicReference<>(State.IDLE);
	private final ServiceRegistration	registration;
	private final Map					viewers	= new ConcurrentHashMap<>();
	private final BundleContext									context;

	RepositoriesViewRefresher() {
		ServiceRegistration reg = null;
		Bundle bundle = FrameworkUtil.getBundle(RepositoriesViewRefresher.class);
		if (bundle != null) {
			context = bundle.getBundleContext();
			if (context != null)
				reg = context.registerService(RepositoryListenerPlugin.class, this, null);
		} else
			context = null;
		this.registration = reg;
	}

	public void refreshRepositories(final RepositoryPlugin target) {
		if (state.updateAndGet(current -> (current == State.IDLE) ? State.BUSY : State.REDO) == State.REDO) {
			return;
		}

		//
		// Since this can delay, we move this to the background
		//
		Central.onAnyWorkspace(ws -> {
			new WorkspaceJob("Updating repositories content") {
				@Override
				public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
					if (monitor == null)
						monitor = new NullProgressMonitor();

					Set repos = new HashSet<>();
					if (target != null)
						repos.add(target);
					else {
						for (RefreshModel m : viewers.values()) {
							repos.addAll(m.getRepositories());
						}
					}

					ensureLoaded(monitor, repos, ws);

					final Map, List> entryRepos = new HashMap<>();

					for (Map.Entry entry : viewers.entrySet()) {
						entryRepos.put(entry, entry.getValue()
							.getRepositories());
					}

					state.set(State.BUSY);

					for (Map.Entry entry : viewers.entrySet()) {

						TreeViewer viewer = entry.getKey();
						viewer.getControl()
							.getDisplay()
							.asyncExec(() -> {
								TreePath[] expandedTreePaths = viewer.getExpandedTreePaths();

								viewer.setInput(entryRepos.get(entry));
							if (expandedTreePaths != null && expandedTreePaths.length > 0)
									viewer.setExpandedTreePaths(expandedTreePaths);
							});

					}

					if (target instanceof WorkspaceRepository) {
						// e.g. new project / project template project was
						// added.
						// refresh the EclipseWorkspaceRepository so that new
						// added project templates show up
						// in the "New Project" wizard (see
						// AbstractNewBndProjectWizard)
						try {
							monitor.subTask("Refresh EclipseWorkspaceRepository to detect new or changed projects");
							Central.getEclipseWorkspaceRepository()
								.refresh();
						} catch (Exception e) {
							// swallow to not interrupt the rest
						}
					}

					if (state.getAndSet(State.IDLE) == State.REDO) {
						refreshRepositories(null);
					}
					return Status.OK_STATUS;
				}
			}.schedule(1000);
		});
	}

	private IStatus ensureLoaded(IProgressMonitor monitor, Collection repos, Workspace ws) {
		int n = 0;
		try {
			final RepositoryPlugin workspaceRepo = ws.getWorkspaceRepository();
			for (RepositoryPlugin repo : repos) {
				if (monitor.isCanceled()) {
					return Status.CANCEL_STATUS;
				}
				monitor.beginTask(repo.getName(), n++);
				if (repo != workspaceRepo) {
					repo.list(null); // looks silly but is here to incur any
										// download time
					continue;
				}
				// We must safely call bnd to list workspace repo
				try {
					ws.readLocked(() -> workspaceRepo.list(null), monitor::isCanceled);
				} catch (TimeoutException | InterruptedException e) {
					return new Status(IStatus.ERROR, Plugin.PLUGIN_ID,
						"Unable to acquire lock to refresh repository " + repo.getName(), e);
				}
			}
		} catch (Exception e) {
			return new Status(IStatus.ERROR, Plugin.PLUGIN_ID, "Exception refreshing repositories", e);
		}
		return Status.OK_STATUS;
	}

	public void addViewer(TreeViewer viewer, RefreshModel model) {
		this.viewers.put(viewer, model);
		Central.onAnyWorkspace(workspace -> new Job("Updating repositories") {
			@Override
			protected IStatus run(IProgressMonitor monitor) {
				List repositories = model.getRepositories();
				Display.getDefault()
					.asyncExec(() -> viewer.setInput(repositories));
				return Status.OK_STATUS;
			}
		}.schedule());
	}

	public void removeViewer(TreeViewer viewer) {
		this.viewers.remove(viewer);
	}

	@Override
	public void bundleAdded(final RepositoryPlugin repository, Jar jar, File file) {
		refreshRepositories(repository);
	}

	@Override
	public void bundleRemoved(final RepositoryPlugin repository, Jar jar, File file) {
		refreshRepositories(repository);
	}

	@Override
	public void repositoryRefreshed(final RepositoryPlugin repository) {
		refreshRepositories(repository);
	}

	@Override
	public void repositoriesRefreshed() {
		refreshRepositories(null);
	}

	public void close() {
		if (registration != null)
			registration.unregister();
	}

	public void setRepositories(final TreeViewer viewer, final RefreshModel refresh) {
		if (state.updateAndGet(current -> (current == State.IDLE) ? State.BUSY : State.REDO) == State.REDO) {
			return;
		}
		Central.onAnyWorkspace(ws -> {
			new WorkspaceJob("Setting repositories") {
				@Override
				public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
					if (monitor == null)
						monitor = new NullProgressMonitor();

					ensureLoaded(monitor, refresh.getRepositories(), ws);

					SWTConcurrencyUtil.execForControl(viewer.getControl(), true, () -> {
						viewer.setInput(refresh.getRepositories());

						if (state.getAndSet(State.IDLE) == State.REDO) {
							refreshRepositories(null);
						}
					});
					return Status.OK_STATUS;
				}
			}.schedule();
		});
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy