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;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
42 * @author Tuukka Lehtonen
44 public class Project extends HintContext implements IProject {
46 private static final Logger LOGGER = LoggerFactory.getLogger(Project.class);
48 private static IProjectFeature[] NONE = {};
50 //private static final String TAG_PROJECTS = "projects";
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>();
57 private final Lock lock = new ReentrantLock();
58 private final AtomicBoolean active = new AtomicBoolean(false);
60 protected Session session;
61 protected Resource resource;
63 public Project(Session session, Resource resource) {
65 throw new NullPointerException("null session");
67 throw new NullPointerException("null resource");
69 this.session = session;
70 this.resource = resource;
71 //System.out.println("NEW PROJECT [" + resource.getResourceId() + "] (" + System.identityHashCode(this) + ")");
74 public void doDispose() {
75 //System.out.println("DISPOSE: " + this + " (" + System.identityHashCode(this) + ")");
82 } catch (ProjectException e1) {
83 // TODO: do something more sensible than print the possible exception!
91 public IProjectFeature[] getFeatures() {
95 IProjectFeature[] features = this.features;
96 return Arrays.copyOf(features, features.length);
99 public void addFeature(IProjectFeature feature) {
103 if (!featureSet.contains(feature)) {
104 featureSet.add(feature);
105 inactiveFeatures.add(feature);
106 features = featureSet.toArray(new IProjectFeature[featureSet.size()]);
114 public void activate() throws ProjectException {
116 throw new IllegalStateException("project is disposed, cannot activate " + this + " (" + System.identityHashCode(this) + ")");
120 if (active.get() == true)
123 while (!inactiveFeatures.isEmpty()) {
124 IProjectFeature feature = inactiveFeatures.getFirst();
125 boolean success = false;
127 LOGGER.info("Configuring project feature {}", feature);
128 feature.setProjectElement(this);
131 inactiveFeatures.removeFirst();
132 activeFeatures.addLast(feature);
137 feature.setProjectElement(null);
142 transactionBarrier();
152 public void deactivate() throws ProjectException {
154 throw new IllegalStateException("project is disposed, cannot deactivate " + this + " (" + System.identityHashCode(this) + ")");
158 if (active.get() == false)
161 while (!activeFeatures.isEmpty()) {
162 IProjectFeature feature = activeFeatures.getLast();
163 boolean success = false;
165 feature.deconfigure();
167 activeFeatures.removeLast();
168 inactiveFeatures.addFirst(feature);
173 feature.setProjectElement(null);
178 transactionBarrier();
187 private void transactionBarrier() throws ProjectException {
188 // IMPORTANT: ensure that all database requests have been
189 // completed before returning from deactivation.
191 session.syncRequest(new WriteOnlyRequest() {
193 public void perform(WriteOnlyGraph graph) throws DatabaseException {
194 throw new CancelTransactionException("barrier");
197 } catch (CancelTransactionException e) {
198 } catch (DatabaseException e) {
199 throw new ProjectException(e);
204 public String toString() {
205 return getClass().getSimpleName() + " [resource=" + get().getResourceId() + "]";
209 public int hashCode() {
210 return get().hashCode();
214 public boolean equals(Object obj) {
217 if (!(obj instanceof Project))
219 final Project other = (Project) obj;
221 // Need to make sure that the resources are from the same session.
222 if (!session.equals(other.session))
226 Resource r2 = other.get();
230 } else if (!r1.equals(r2))
237 public Session getSession() {
242 public Resource get() {
246 public RequestProcessor getGraphRequestProcessor() {
247 MergingGraphRequestProcessor mgrp = session.peekService(MergingGraphRequestProcessor.class);
248 return mgrp != null ? mgrp : session;
251 // IDisposable implementation (copied from AbstractDisposable)
253 SyncListenerList<IDisposeListener> disposeListeners = null;
255 private volatile DisposeState disposeStatus = DisposeState.Alive;
257 protected void assertNotDisposed() {
259 throw new IllegalStateException(this + " is disposed");
263 public DisposeState getDisposeState() {
264 return disposeStatus;
268 public boolean isDisposed() {
269 return disposeStatus == DisposeState.Disposed;
272 public boolean isAlive() {
273 return disposeStatus == DisposeState.Alive;
277 public void dispose() {
281 if (disposeStatus == DisposeState.Disposing)
284 disposeStatus = DisposeState.Disposing;
295 disposeStatus = DisposeState.Disposed;
300 * Disposes if not disposed
303 public void safeDispose() {
307 if (disposeStatus != DisposeState.Alive)
309 disposeStatus = DisposeState.Disposing;
320 disposeStatus = DisposeState.Disposed;
324 protected boolean hasDisposeListeners() {
325 return disposeListeners!=null && !disposeListeners.isEmpty();
328 private final static Method onDisposed = SyncListenerList.getMethod(IDisposeListener.class, "onDisposed");
330 private void fireDisposed() {
331 if (disposeListeners==null) return;
332 disposeListeners.fireEventSync(onDisposed, this);
335 @SuppressWarnings("unused")
336 private void fireDisposedAsync() {
337 if (disposeListeners==null) return;
338 disposeListeners.fireEventAsync(onDisposed, this);
342 public void addDisposeListener(IDisposeListener listener) {
343 lazyGetListenerList().add(listener);
347 public void addDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
348 lazyGetListenerList().add(thread, listener);
352 public void removeDisposeListener(IDisposeListener listener) {
353 if (disposeListeners==null) return;
354 disposeListeners.remove(listener);
358 public void removeDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
359 if (disposeListeners==null) return;
360 disposeListeners.remove(thread, listener);
363 private synchronized SyncListenerList<IDisposeListener> lazyGetListenerList() {
364 if (disposeListeners==null)
365 disposeListeners = new SyncListenerList<IDisposeListener>(IDisposeListener.class);
366 return disposeListeners;