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