blob: 417b949b3cbf69374c011ce4464a761929fd6492 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2007 Oracle Corporation and others.
* 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:
* Oracle Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.jsf.facesconfig.ui;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jst.jsf.facesconfig.ui.util.WebrootUtil;
import org.eclipse.jst.jsf.facesconfig.util.FacesConfigArtifactEdit;
import org.eclipse.ui.PlatformUI;
/**
* Centralizes logic to load the faces config model off of the UI thread for
* the FacesConfig Editor.
*
* Also encapsulates the lifecycle for the instance of the faces artifact
* created for its editor. All creation, access and destruction of the artifact
* should be centralized through this class
*
* @author cbateman
*
*/
class ModelLoader
{
private FacesConfigArtifactEdit _edit;
private Job _loadModelJob;
private CountDownLatch _modelLoaded = new CountDownLatch(1);
/**
* @return the artifact edit or null if not loaded. Should only be called
* after load() is called and has executed its callback
*/
public synchronized FacesConfigArtifactEdit getEdit() {
return _edit;
}
private synchronized void setEdit(FacesConfigArtifactEdit edit)
{
_edit = edit;
}
void waitForLoad(long timeoutMs) throws InterruptedException
{
_modelLoaded.await(timeoutMs, TimeUnit.MILLISECONDS);
}
/**
* Dispose of the model and any unfinished loading operations
*
* Must be run on the UI thread.
*/
public synchronized void dispose()
{
assertOnDisplayThread();
// if the load model job has not completed, cancel it
if (_loadModelJob != null
&& _loadModelJob.getResult() == null)
{
_loadModelJob.cancel();
}
if (_edit != null)
{
_edit.dispose();
//System.out.println("FacesConfigEditor.dispose(): isDisposed == "+_edit.isDisposed());
}
}
/**
* Load the model file located by path in project. Must be called from the UI thread.
*
* Method does not block.
*
* @param project
* @param path
* @param isWebProject
* @param signalComplete to be asyncExec'd on the UI thread when the model is loaded
*/
public void load(final IProject project, final IPath path, final boolean isWebProject, final ModelLoaderComplete signalComplete)
{
assertOnDisplayThread();
_loadModelJob = new ModelLoaderJob(project, path, isWebProject, signalComplete);
_loadModelJob.schedule();
}
private class ModelLoaderJob extends Job
{
private final IProject _project;
private final IPath _path;
private final ModelLoaderComplete _runnable;
private final boolean _isWebProject;
ModelLoaderJob(final IProject project, final IPath path, final boolean isWebProject, final ModelLoaderComplete signalComplete)
{
super(EditorMessages.ModelLoader_LoadingModelJobName);
_project = project;
_path = path;
_runnable = signalComplete;
_isWebProject = isWebProject;
}
@Override
protected IStatus run(IProgressMonitor monitor)
{
FacesConfigArtifactEdit artifactEdit = loadModel(_project, _path);
// synchrnoize on the ModelLoader. Ensure that any call to dispose()
// that occurs before we set the edit is done atomically.
synchronized(ModelLoader.this)
{
// only bother with this if the task hasn't been signalled for cancel
if (!monitor.isCanceled())
{
setEdit(artifactEdit);
_runnable.setFacesConfigArtifactEdit(artifactEdit);
// finish as quickly possible; we are holding the ModelLoader
// lock so we must ensure that we don't block.
// NEVER USE syncExec here.
PlatformUI.getWorkbench().getDisplay().asyncExec(_runnable);
}
// if we were cancelled, then dispose of the artifact edit
else
{
if (artifactEdit != null)
{
artifactEdit.dispose();
}
}
}
// signal that we are done loading
_modelLoaded.countDown();
return Status.OK_STATUS;
}
/**
* Loads the configuration model from the given path.
*
*/
private FacesConfigArtifactEdit loadModel(IProject project, IPath modelPath)
{
if (_isWebProject)
{
IFolder webContentFolder = WebrootUtil.getWebContentFolder(project);
Assert
.isTrue(webContentFolder != null
&& webContentFolder.exists());
IPath relativePath = modelPath;
if (webContentFolder.getFullPath().isPrefixOf(modelPath)) {
relativePath = modelPath.removeFirstSegments(webContentFolder
.getFullPath().segmentCount());
}
return FacesConfigArtifactEdit
.getFacesConfigArtifactEditForWrite(project, relativePath
.toString());
}
return null;
}
}
abstract static class ModelLoaderComplete implements Runnable
{
private FacesConfigArtifactEdit _edit;
private void setFacesConfigArtifactEdit(FacesConfigArtifactEdit edit)
{
_edit = edit;
}
public final void run()
{
assertOnDisplayThread();
doRun(_edit);
}
/**
* Called by the runnable. Implementer should _not_ cache the edit variable
* edit may be null
*
* @param edit
*/
protected abstract void doRun(FacesConfigArtifactEdit edit);
}
private static void assertOnDisplayThread()
{
if (Thread.currentThread() != PlatformUI.getWorkbench().getDisplay().getThread())
{
throw new IllegalStateException("ModelLoaderComplete must be called on the UI thread"); //$NON-NLS-1$
}
}
}