blob: 2b8ff7b337fea870ef95b0c26d8b66b749fac1c6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.sdk.internal.workspace;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.osgi.service.resolver.State;
import org.eclipse.osgi.service.resolver.StateDelta;
import org.eclipse.pde.internal.core.IPluginModelListener;
import org.eclipse.pde.internal.core.IStateDeltaListener;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.PluginModelDelta;
import org.eclipse.scout.commons.EventListenerList;
import org.eclipse.scout.commons.job.JobEx;
import org.eclipse.scout.sdk.Texts;
import org.eclipse.scout.sdk.internal.ScoutSdk;
import org.eclipse.scout.sdk.util.jdt.JdtUtility;
import org.eclipse.scout.sdk.util.log.ScoutStatus;
import org.eclipse.scout.sdk.workspace.IScoutBundleGraph;
import org.eclipse.scout.sdk.workspace.IScoutWorkspace;
import org.eclipse.scout.sdk.workspace.IScoutWorkspaceListener;
import org.eclipse.scout.sdk.workspace.ScoutWorkspaceEvent;
@SuppressWarnings("restriction")
public final class ScoutWorkspace implements IScoutWorkspace {
static final String BUNDLE_GRAPH_REBUILD_JOB_FAMILY = "rebuildScoutBundleGraphJobFamily";
private static final ScoutWorkspace INSTANCE = new ScoutWorkspace();
private final ScoutBundleGraph m_bundleGraph;
private final P_PluginModelListener m_pluginModelListener;
private EventListenerList m_eventListeners;
private boolean m_isInitialized;
private ScoutWorkspace() {
m_isInitialized = false;
m_eventListeners = new EventListenerList();
m_bundleGraph = new ScoutBundleGraph();
m_pluginModelListener = new P_PluginModelListener();
// bundle graph rebuild listener
PDECore pdeCore = PDECore.getDefault();
if (pdeCore != null) {
pdeCore.getModelManager().addPluginModelListener(m_pluginModelListener);
pdeCore.getModelManager().addStateDeltaListener(m_pluginModelListener);
}
// initialize bundle graph
P_BundleGraphRebuildJob j = createBundleGraphRebuildJob();
j.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
fireWorkspaceEvent(new ScoutWorkspaceEvent(ScoutWorkspace.this, ScoutWorkspaceEvent.TYPE_WORKSPACE_INITIALIZED, null));
m_isInitialized = true;
}
});
j.schedule();
}
public synchronized void dispose() {
m_isInitialized = false;
PDECore pdeCore = PDECore.getDefault();
if (m_pluginModelListener != null && pdeCore != null) {
pdeCore.getModelManager().removePluginModelListener(m_pluginModelListener);
pdeCore.getModelManager().removeStateDeltaListener(m_pluginModelListener);
}
Job.getJobManager().cancel(BUNDLE_GRAPH_REBUILD_JOB_FAMILY);
P_BundleGraphRebuildShutdownJob job = new P_BundleGraphRebuildShutdownJob();
job.schedule();
try {
job.join(20000);
}
catch (InterruptedException e) {
}
m_bundleGraph.dispose();
m_eventListeners = new EventListenerList(); // loose all old listeners
}
public static ScoutWorkspace getInstance() {
return INSTANCE;
}
@Override
public synchronized void addWorkspaceListener(IScoutWorkspaceListener listener) {
m_eventListeners.add(IScoutWorkspaceListener.class, listener);
}
@Override
public synchronized void removeWorkspaceListener(IScoutWorkspaceListener listener) {
m_eventListeners.remove(IScoutWorkspaceListener.class, listener);
}
@Override
public IScoutBundleGraph getBundleGraph() {
return m_bundleGraph;
}
ScoutBundleGraph getBundleGraphInternal() {
return m_bundleGraph;
}
synchronized int getNumBundleGraphRebuildJobs() {
return Job.getJobManager().find(BUNDLE_GRAPH_REBUILD_JOB_FAMILY).length;
}
/**
* Schedules a job that rebuilds the scout bundle graph (asynchronously).
* After finishing, changes are reported using the {@link IScoutWorkspaceListener}
*
* @see IScoutWorkspace#addWorkspaceListener(IScoutWorkspaceListener)
*/
public void rebuildGraph() {
final P_BundleGraphRebuildJob j = createBundleGraphRebuildJob();
j.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
if (event.getResult().isOK()) {
fireWorkspaceEvents(j.getEventCollector());
}
}
});
synchronized (this) {
Job.getJobManager().cancel(BUNDLE_GRAPH_REBUILD_JOB_FAMILY);
j.schedule();
}
}
private P_BundleGraphRebuildJob createBundleGraphRebuildJob() {
P_BundleGraphRebuildJob j = new P_BundleGraphRebuildJob();
j.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
if (event.getResult().isOK()) {
for (String detectedIssue : m_bundleGraph.getDependencyIssues()) {
ScoutSdk.logWarning(detectedIssue);
}
}
}
});
return j;
}
private void fireWorkspaceEvents(ScoutWorkspaceEventList events) {
for (ScoutWorkspaceEvent e : events.getAllEvents()) {
fireWorkspaceEvent(e);
}
}
private void fireWorkspaceEvent(ScoutWorkspaceEvent e) {
for (IScoutWorkspaceListener l : m_eventListeners.getListeners(IScoutWorkspaceListener.class)) {
try {
l.workspaceChanged(e);
}
catch (Exception t) {
ScoutSdk.logError("error during listener notification '" + l.getClass().getName() + "'.", t);
}
}
}
private static final class P_BundleGraphRebuildShutdownJob extends JobEx {
private P_BundleGraphRebuildShutdownJob() {
super("wait for bundle graph job to shutdown");
setSystem(true);
setUser(false);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
JdtUtility.waitForJobFamily(BUNDLE_GRAPH_REBUILD_JOB_FAMILY);
return Status.CANCEL_STATUS;
}
}
private final class P_BundleGraphRebuildJob extends JobEx {
private final ScoutWorkspaceEventList m_eventCollector;
private P_BundleGraphRebuildJob() {
super(Texts.get("RebuildingScoutBundleGraph") + "...");
setUser(false);
// [mvi] Important: do not set a rule for this job:
// When in this job a new type must be found, the search-engine is started.
// This may cause the eclipse project to be touched (see org.eclipse.core.internal.resources.Project#touch()).
// In this touch method a rule is specified which leads to an IllegalArgumentException in org.eclipse.core.internal.jobs.ThreadJob#push() when there is already a rule set.
// If a rule must be added, ensure it contains all workspace projects (see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(ISchedulingRule rule)).
m_eventCollector = new ScoutWorkspaceEventList(ScoutWorkspace.this);
}
@Override
public boolean belongsTo(Object family) {
return BUNDLE_GRAPH_REBUILD_JOB_FAMILY.equals(family);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
boolean completed = m_bundleGraph.build(getEventCollector(), monitor);
if (completed) {
return Status.OK_STATUS;
}
else {
return Status.CANCEL_STATUS;
}
}
catch (Exception t) {
ScoutSdk.logError("Unable to build the Scout Bundle Graph.", t);
return new ScoutStatus(t);
}
}
public ScoutWorkspaceEventList getEventCollector() {
return m_eventCollector;
}
}
private final class P_PluginModelListener implements IPluginModelListener, IStateDeltaListener {
@Override
public void modelsChanged(PluginModelDelta delta) {
try {
if (containsInterestingProjects(delta)) {
rebuildGraph();
}
}
catch (CoreException e) {
ScoutSdk.logError(e);
}
}
private boolean containsInterestingProjects(PluginModelDelta delta) throws CoreException {
return delta.getChangedEntries().length > 0 || delta.getAddedEntries().length > 0 || delta.getRemovedEntries().length > 0;
}
@Override
public void stateResolved(StateDelta delta) {
}
@Override
public void stateChanged(State newState) {
rebuildGraph();
}
}
@Override
public boolean isInitialized() {
return m_isInitialized;
}
}