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