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