Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 417b949b3cbf69374c011ce4464a761929fd6492 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*******************************************************************************
 * 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$
        }
    }
}

Back to the top