1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.project.impl;
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;
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;
40 * @author Tuukka Lehtonen
42 public class Project extends HintContext implements IProject {
44 private static IProjectFeature[] NONE = {};
46 //private static final String TAG_PROJECTS = "projects";
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>();
53 private final Lock lock = new ReentrantLock();
54 private final AtomicBoolean active = new AtomicBoolean(false);
56 protected Session session;
57 protected Resource resource;
59 public Project(Session session, Resource resource) {
61 throw new NullPointerException("null session");
63 throw new NullPointerException("null resource");
65 this.session = session;
66 this.resource = resource;
67 //System.out.println("NEW PROJECT [" + resource.getResourceId() + "] (" + System.identityHashCode(this) + ")");
70 public void doDispose() {
71 //System.out.println("DISPOSE: " + this + " (" + System.identityHashCode(this) + ")");
78 } catch (ProjectException e1) {
79 // TODO: do something more sensible than print the possible exception!
87 public IProjectFeature[] getFeatures() {
91 IProjectFeature[] features = this.features;
92 return Arrays.copyOf(features, features.length);
95 public void addFeature(IProjectFeature feature) {
99 if (!featureSet.contains(feature)) {
100 featureSet.add(feature);
101 inactiveFeatures.add(feature);
102 features = featureSet.toArray(new IProjectFeature[featureSet.size()]);
110 public void activate() throws ProjectException {
112 throw new IllegalStateException("project is disposed, cannot activate " + this + " (" + System.identityHashCode(this) + ")");
116 if (active.get() == true)
119 while (!inactiveFeatures.isEmpty()) {
120 IProjectFeature feature = inactiveFeatures.getFirst();
121 boolean success = false;
123 feature.setProjectElement(this);
126 inactiveFeatures.removeFirst();
127 activeFeatures.addLast(feature);
132 feature.setProjectElement(null);
137 transactionBarrier();
147 public void deactivate() throws ProjectException {
149 throw new IllegalStateException("project is disposed, cannot deactivate " + this + " (" + System.identityHashCode(this) + ")");
153 if (active.get() == false)
156 while (!activeFeatures.isEmpty()) {
157 IProjectFeature feature = activeFeatures.getLast();
158 boolean success = false;
160 feature.deconfigure();
162 activeFeatures.removeLast();
163 inactiveFeatures.addFirst(feature);
168 feature.setProjectElement(null);
173 transactionBarrier();
182 private void transactionBarrier() throws ProjectException {
183 // IMPORTANT: ensure that all database requests have been
184 // completed before returning from deactivation.
186 session.syncRequest(new WriteOnlyRequest() {
188 public void perform(WriteOnlyGraph graph) throws DatabaseException {
189 throw new CancelTransactionException("barrier");
192 } catch (CancelTransactionException e) {
193 } catch (DatabaseException e) {
194 throw new ProjectException(e);
199 public String toString() {
200 return getClass().getSimpleName() + " [resource=" + get().getResourceId() + "]";
204 public int hashCode() {
205 return get().hashCode();
209 public boolean equals(Object obj) {
212 if (!(obj instanceof Project))
214 final Project other = (Project) obj;
216 // Need to make sure that the resources are from the same session.
217 if (!session.equals(other.session))
221 Resource r2 = other.get();
225 } else if (!r1.equals(r2))
232 public Session getSession() {
237 public Resource get() {
241 public RequestProcessor getGraphRequestProcessor() {
242 MergingGraphRequestProcessor mgrp = session.peekService(MergingGraphRequestProcessor.class);
243 return mgrp != null ? mgrp : session;
246 // IDisposable implementation (copied from AbstractDisposable)
248 SyncListenerList<IDisposeListener> disposeListeners = null;
250 private volatile DisposeState disposeStatus = DisposeState.Alive;
252 protected void assertNotDisposed() {
254 throw new IllegalStateException(this + " is disposed");
258 public DisposeState getDisposeState() {
259 return disposeStatus;
263 public boolean isDisposed() {
264 return disposeStatus == DisposeState.Disposed;
267 public boolean isAlive() {
268 return disposeStatus == DisposeState.Alive;
272 public void dispose() {
276 if (disposeStatus == DisposeState.Disposing)
279 disposeStatus = DisposeState.Disposing;
290 disposeStatus = DisposeState.Disposed;
295 * Disposes if not disposed
298 public void safeDispose() {
302 if (disposeStatus != DisposeState.Alive)
304 disposeStatus = DisposeState.Disposing;
315 disposeStatus = DisposeState.Disposed;
319 protected boolean hasDisposeListeners() {
320 return disposeListeners!=null && !disposeListeners.isEmpty();
323 private final static Method onDisposed = SyncListenerList.getMethod(IDisposeListener.class, "onDisposed");
325 private void fireDisposed() {
326 if (disposeListeners==null) return;
327 disposeListeners.fireEventSync(onDisposed, this);
330 @SuppressWarnings("unused")
331 private void fireDisposedAsync() {
332 if (disposeListeners==null) return;
333 disposeListeners.fireEventAsync(onDisposed, this);
337 public void addDisposeListener(IDisposeListener listener) {
338 lazyGetListenerList().add(listener);
342 public void addDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
343 lazyGetListenerList().add(thread, listener);
347 public void removeDisposeListener(IDisposeListener listener) {
348 if (disposeListeners==null) return;
349 disposeListeners.remove(listener);
353 public void removeDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
354 if (disposeListeners==null) return;
355 disposeListeners.remove(thread, listener);
358 private synchronized SyncListenerList<IDisposeListener> lazyGetListenerList() {
359 if (disposeListeners==null)
360 disposeListeners = new SyncListenerList<IDisposeListener>(IDisposeListener.class);
361 return disposeListeners;