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 e) {
83 LOGGER.error("Could not dispose Project {}", this, e);
90 public IProjectFeature[] getFeatures() {
94 IProjectFeature[] features = this.features;
95 return Arrays.copyOf(features, features.length);
98 public void addFeature(IProjectFeature feature) {
102 if (!featureSet.contains(feature)) {
103 featureSet.add(feature);
104 inactiveFeatures.add(feature);
105 features = featureSet.toArray(new IProjectFeature[featureSet.size()]);
113 public void activate() throws ProjectException {
115 throw new IllegalStateException("project is disposed, cannot activate " + this + " (" + System.identityHashCode(this) + ")");
119 if (active.get() == true)
122 while (!inactiveFeatures.isEmpty()) {
123 IProjectFeature feature = inactiveFeatures.getFirst();
124 boolean success = false;
126 LOGGER.info("Configuring project feature {}", feature);
127 feature.setProjectElement(this);
130 inactiveFeatures.removeFirst();
131 activeFeatures.addLast(feature);
136 feature.setProjectElement(null);
141 transactionBarrier();
151 public void deactivate() throws ProjectException {
153 throw new IllegalStateException("project is disposed, cannot deactivate " + this + " (" + System.identityHashCode(this) + ")");
157 if (active.get() == false)
160 while (!activeFeatures.isEmpty()) {
161 IProjectFeature feature = activeFeatures.getLast();
162 boolean success = false;
164 feature.deconfigure();
166 activeFeatures.removeLast();
167 inactiveFeatures.addFirst(feature);
172 feature.setProjectElement(null);
177 transactionBarrier();
186 private void transactionBarrier() throws ProjectException {
187 // IMPORTANT: ensure that all database requests have been
188 // completed before returning from deactivation.
190 session.syncRequest(new WriteOnlyRequest() {
192 public void perform(WriteOnlyGraph graph) throws DatabaseException {
193 throw new CancelTransactionException("barrier");
196 } catch (CancelTransactionException e) {
197 } catch (DatabaseException e) {
198 throw new ProjectException(e);
203 public String toString() {
204 return getClass().getSimpleName() + " [resource=" + get().getResourceId() + "]";
208 public int hashCode() {
209 return get().hashCode();
213 public boolean equals(Object obj) {
216 if (!(obj instanceof Project))
218 final Project other = (Project) obj;
220 // Need to make sure that the resources are from the same session.
221 if (!session.equals(other.session))
225 Resource r2 = other.get();
229 } else if (!r1.equals(r2))
236 public Session getSession() {
241 public Resource get() {
245 public RequestProcessor getGraphRequestProcessor() {
246 MergingGraphRequestProcessor mgrp = session.peekService(MergingGraphRequestProcessor.class);
247 return mgrp != null ? mgrp : session;
250 // IDisposable implementation (copied from AbstractDisposable)
252 SyncListenerList<IDisposeListener> disposeListeners = null;
254 private volatile DisposeState disposeStatus = DisposeState.Alive;
256 protected void assertNotDisposed() {
258 throw new IllegalStateException(this + " is disposed");
262 public DisposeState getDisposeState() {
263 return disposeStatus;
267 public boolean isDisposed() {
268 return disposeStatus == DisposeState.Disposed;
271 public boolean isAlive() {
272 return disposeStatus == DisposeState.Alive;
276 public void dispose() {
280 if (disposeStatus == DisposeState.Disposing)
283 disposeStatus = DisposeState.Disposing;
294 disposeStatus = DisposeState.Disposed;
299 * Disposes if not disposed
302 public void safeDispose() {
306 if (disposeStatus != DisposeState.Alive)
308 disposeStatus = DisposeState.Disposing;
319 disposeStatus = DisposeState.Disposed;
323 protected boolean hasDisposeListeners() {
324 return disposeListeners!=null && !disposeListeners.isEmpty();
327 private final static Method onDisposed = SyncListenerList.getMethod(IDisposeListener.class, "onDisposed");
329 private void fireDisposed() {
330 if (disposeListeners==null) return;
331 disposeListeners.fireEventSync(onDisposed, this);
334 @SuppressWarnings("unused")
335 private void fireDisposedAsync() {
336 if (disposeListeners==null) return;
337 disposeListeners.fireEventAsync(onDisposed, this);
341 public void addDisposeListener(IDisposeListener listener) {
342 lazyGetListenerList().add(listener);
346 public void addDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
347 lazyGetListenerList().add(thread, listener);
351 public void removeDisposeListener(IDisposeListener listener) {
352 if (disposeListeners==null) return;
353 disposeListeners.remove(listener);
357 public void removeDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
358 if (disposeListeners==null) return;
359 disposeListeners.remove(thread, listener);
362 private synchronized SyncListenerList<IDisposeListener> lazyGetListenerList() {
363 if (disposeListeners==null)
364 disposeListeners = new SyncListenerList<IDisposeListener>(IDisposeListener.class);
365 return disposeListeners;