]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.project/src/org/simantics/project/impl/Project.java
Speeding up platform startup time
[simantics/platform.git] / bundles / org.simantics.project / src / org / simantics / project / impl / Project.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.project.impl;
13
14 import java.lang.reflect.Method;
15 import java.util.Arrays;
16 import java.util.LinkedList;
17 import java.util.concurrent.atomic.AtomicBoolean;
18 import java.util.concurrent.locks.Lock;
19 import java.util.concurrent.locks.ReentrantLock;
20
21 import org.simantics.db.RequestProcessor;
22 import org.simantics.db.Resource;
23 import org.simantics.db.Session;
24 import org.simantics.db.WriteOnlyGraph;
25 import org.simantics.db.common.processor.MergingGraphRequestProcessor;
26 import org.simantics.db.common.request.WriteOnlyRequest;
27 import org.simantics.db.exception.CancelTransactionException;
28 import org.simantics.db.exception.DatabaseException;
29 import org.simantics.project.IProject;
30 import org.simantics.project.exception.ProjectException;
31 import org.simantics.project.features.IProjectFeature;
32 import org.simantics.utils.datastructures.disposable.DisposeState;
33 import org.simantics.utils.datastructures.disposable.IDisposeListener;
34 import org.simantics.utils.datastructures.hints.HintContext;
35 import org.simantics.utils.threads.IThreadWorkQueue;
36 import org.simantics.utils.threads.SyncListenerList;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40
41 /**
42  * @author Tuukka Lehtonen
43  */
44 public class Project extends HintContext implements IProject {
45
46     private static final Logger LOGGER = LoggerFactory.getLogger(Project.class);
47
48     private static IProjectFeature[]          NONE         = {};
49
50     //private static final String               TAG_PROJECTS = "projects";
51
52     private IProjectFeature[]                 features         = NONE;
53     private final LinkedList<IProjectFeature> featureSet       = new LinkedList<IProjectFeature>();
54     private final LinkedList<IProjectFeature> inactiveFeatures = new LinkedList<IProjectFeature>();
55     private final LinkedList<IProjectFeature> activeFeatures   = new LinkedList<IProjectFeature>();
56
57     private final Lock                        lock             = new ReentrantLock();
58     private final AtomicBoolean               active           = new AtomicBoolean(false);
59
60     protected Session                         session;
61     protected Resource                        resource;
62
63     public Project(Session session, Resource resource) {
64         if (session == null)
65             throw new NullPointerException("null session");
66         if (resource == null)
67             throw new NullPointerException("null resource");
68
69         this.session = session;
70         this.resource = resource;
71         //System.out.println("NEW PROJECT [" + resource.getResourceId() + "] (" + System.identityHashCode(this) + ")");
72     }
73
74     public void doDispose() {
75         //System.out.println("DISPOSE: " + this + " (" + System.identityHashCode(this) + ")");
76         lock.lock();
77         try {
78             deactivate();
79
80             featureSet.clear();
81             features = NONE;
82         } catch (ProjectException e1) {
83             // TODO: do something more sensible than print the possible exception!
84             e1.printStackTrace();
85         } finally {
86             lock.unlock();
87         }
88     }
89
90     @Override
91     public IProjectFeature[] getFeatures() {
92         assertNotDisposed();
93
94         // Defensive copy.
95         IProjectFeature[] features = this.features;
96         return Arrays.copyOf(features, features.length);
97     }
98
99     public void addFeature(IProjectFeature feature) {
100         assertNotDisposed();
101         lock.lock();
102         try {
103             if (!featureSet.contains(feature)) {
104                 featureSet.add(feature);
105                 inactiveFeatures.add(feature);
106                 features = featureSet.toArray(new IProjectFeature[featureSet.size()]);
107             }
108         } finally {
109             lock.unlock();
110         }
111     }
112
113     @Override
114     public void activate() throws ProjectException {
115         if (isDisposed())
116             throw new IllegalStateException("project is disposed, cannot activate " + this + " (" + System.identityHashCode(this) + ")");
117
118         lock.lock();
119         try {
120             if (active.get() == true)
121                 return;
122
123             while (!inactiveFeatures.isEmpty()) {
124                 IProjectFeature feature = inactiveFeatures.getFirst();
125                 boolean success = false;
126                 try {
127                     LOGGER.info("Configuring project feature {}", feature);
128                     feature.setProjectElement(this);
129                     feature.configure();
130
131                     inactiveFeatures.removeFirst();
132                     activeFeatures.addLast(feature);
133
134                     success = true;
135                 } finally {
136                     if (!success)
137                         feature.setProjectElement(null);
138                 }
139             }
140
141             try {
142                 transactionBarrier();
143             } finally {
144                 active.set(true);
145             }
146         } finally {
147             lock.unlock();
148         }
149     }
150
151     @Override
152     public void deactivate() throws ProjectException {
153         if (isDisposed())
154             throw new IllegalStateException("project is disposed, cannot deactivate " + this + " (" + System.identityHashCode(this) + ")");
155
156         lock.lock();
157         try {
158             if (active.get() == false)
159                 return;
160
161             while (!activeFeatures.isEmpty()) {
162                 IProjectFeature feature = activeFeatures.getLast();
163                 boolean success = false;
164                 try {
165                     feature.deconfigure();
166
167                     activeFeatures.removeLast();
168                     inactiveFeatures.addFirst(feature);
169
170                     success = true;
171                 } finally {
172                     if (success)
173                         feature.setProjectElement(null);
174                 }
175             }
176
177             try {
178                 transactionBarrier();
179             } finally {
180                 active.set(false);
181             }
182         } finally {
183             lock.unlock();
184         }
185     }
186
187     private void transactionBarrier() throws ProjectException {
188         // IMPORTANT: ensure that all database requests have been
189         // completed before returning from deactivation.
190         try {
191             session.syncRequest(new WriteOnlyRequest() {
192                 @Override
193                 public void perform(WriteOnlyGraph graph) throws DatabaseException {
194                     throw new CancelTransactionException("barrier");
195                 }
196             });
197         } catch (CancelTransactionException e) {
198         } catch (DatabaseException e) {
199             throw new ProjectException(e);
200         }
201     }
202
203     @Override
204     public String toString() {
205         return getClass().getSimpleName() + " [resource=" + get().getResourceId() + "]";
206     }
207
208     @Override
209     public int hashCode() {
210         return get().hashCode();
211     }
212
213     @Override
214     public boolean equals(Object obj) {
215         if (this == obj)
216             return true;
217         if (!(obj instanceof Project))
218             return false;
219         final Project other = (Project) obj;
220
221         // Need to make sure that the resources are from the same session.
222         if (!session.equals(other.session))
223             return false;
224
225         Resource r1 = get();
226         Resource r2 = other.get();
227         if (r1 == null) {
228             if (r2 != null)
229                 return false;
230         } else if (!r1.equals(r2))
231             return false;
232
233         return true;
234     }
235
236     @Override
237     public Session getSession() {
238         return session;
239     }
240
241     @Override
242     public Resource get() {
243         return resource;
244     }
245
246     public RequestProcessor getGraphRequestProcessor() {
247         MergingGraphRequestProcessor mgrp = session.peekService(MergingGraphRequestProcessor.class);
248         return mgrp != null ? mgrp : session;
249     }
250
251     // IDisposable implementation (copied from AbstractDisposable)
252
253     SyncListenerList<IDisposeListener> disposeListeners = null;
254
255     private volatile DisposeState disposeStatus = DisposeState.Alive;
256
257     protected void assertNotDisposed() {
258         if (isDisposed())
259             throw new IllegalStateException(this + " is disposed");
260     }
261
262     @Override
263     public DisposeState getDisposeState() {
264         return disposeStatus;
265     }
266
267     @Override
268     public boolean isDisposed() {
269         return disposeStatus == DisposeState.Disposed;
270     }
271
272     public boolean isAlive() {
273         return disposeStatus == DisposeState.Alive;
274     }
275
276     @Override
277     public void dispose() {
278         try {
279             lock.lock();
280             try {
281                 if (disposeStatus == DisposeState.Disposing)
282                     return;
283                 assertNotDisposed();
284                 disposeStatus = DisposeState.Disposing;
285             } finally {
286                 lock.unlock();
287             }
288
289             try {
290                 fireDisposed();
291             } finally {
292                 doDispose();
293             }
294         } finally {
295             disposeStatus = DisposeState.Disposed;
296         }
297     }
298
299     /**
300      * Disposes if not disposed
301      */
302     @Override
303     public void safeDispose() {
304         try {
305             lock.lock();
306             try {
307                 if (disposeStatus != DisposeState.Alive)
308                     return;
309                 disposeStatus = DisposeState.Disposing;
310             } finally {
311                 lock.unlock();
312             }
313
314             try {
315                 fireDisposed();
316             } finally {
317                 doDispose();
318             }
319         } finally {
320             disposeStatus = DisposeState.Disposed;
321         }
322     }
323
324     protected boolean hasDisposeListeners() {
325         return disposeListeners!=null && !disposeListeners.isEmpty();
326     }
327
328     private final static Method onDisposed = SyncListenerList.getMethod(IDisposeListener.class, "onDisposed");
329
330     private void fireDisposed() {
331         if (disposeListeners==null) return;
332         disposeListeners.fireEventSync(onDisposed, this);
333     }
334
335     @SuppressWarnings("unused")
336     private void fireDisposedAsync() {
337         if (disposeListeners==null) return;
338         disposeListeners.fireEventAsync(onDisposed, this);
339     }
340
341     @Override
342     public void addDisposeListener(IDisposeListener listener) {
343         lazyGetListenerList().add(listener);
344     }
345
346     @Override
347     public void addDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
348         lazyGetListenerList().add(thread, listener);
349     }
350
351     @Override
352     public void removeDisposeListener(IDisposeListener listener) {
353         if (disposeListeners==null) return;
354         disposeListeners.remove(listener);
355     }
356
357     @Override
358     public void removeDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
359         if (disposeListeners==null) return;
360         disposeListeners.remove(thread, listener);
361     }
362
363     private synchronized SyncListenerList<IDisposeListener> lazyGetListenerList() {
364         if (disposeListeners==null)
365             disposeListeners = new SyncListenerList<IDisposeListener>(IDisposeListener.class);
366         return disposeListeners;
367     }
368
369 }