-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.project.impl;\r
-\r
-import java.lang.reflect.Method;\r
-import java.util.Arrays;\r
-import java.util.LinkedList;\r
-import java.util.concurrent.atomic.AtomicBoolean;\r
-import java.util.concurrent.locks.Lock;\r
-import java.util.concurrent.locks.ReentrantLock;\r
-\r
-import org.simantics.db.RequestProcessor;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Session;\r
-import org.simantics.db.WriteOnlyGraph;\r
-import org.simantics.db.common.processor.MergingGraphRequestProcessor;\r
-import org.simantics.db.common.request.WriteOnlyRequest;\r
-import org.simantics.db.exception.CancelTransactionException;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.project.IProject;\r
-import org.simantics.project.exception.ProjectException;\r
-import org.simantics.project.features.IProjectFeature;\r
-import org.simantics.utils.datastructures.disposable.DisposeState;\r
-import org.simantics.utils.datastructures.disposable.IDisposeListener;\r
-import org.simantics.utils.datastructures.hints.HintContext;\r
-import org.simantics.utils.threads.IThreadWorkQueue;\r
-import org.simantics.utils.threads.SyncListenerList;\r
-\r
-\r
-/**\r
- * @author Tuukka Lehtonen\r
- */\r
-public class Project extends HintContext implements IProject {\r
-\r
- private static IProjectFeature[] NONE = {};\r
-\r
- //private static final String TAG_PROJECTS = "projects";\r
-\r
- private IProjectFeature[] features = NONE;\r
- private final LinkedList<IProjectFeature> featureSet = new LinkedList<IProjectFeature>();\r
- private final LinkedList<IProjectFeature> inactiveFeatures = new LinkedList<IProjectFeature>();\r
- private final LinkedList<IProjectFeature> activeFeatures = new LinkedList<IProjectFeature>();\r
-\r
- private final Lock lock = new ReentrantLock();\r
- private final AtomicBoolean active = new AtomicBoolean(false);\r
-\r
- protected Session session;\r
- protected Resource resource;\r
-\r
- public Project(Session session, Resource resource) {\r
- if (session == null)\r
- throw new NullPointerException("null session");\r
- if (resource == null)\r
- throw new NullPointerException("null resource");\r
-\r
- this.session = session;\r
- this.resource = resource;\r
- //System.out.println("NEW PROJECT [" + resource.getResourceId() + "] (" + System.identityHashCode(this) + ")");\r
- }\r
-\r
- public void doDispose() {\r
- //System.out.println("DISPOSE: " + this + " (" + System.identityHashCode(this) + ")");\r
- lock.lock();\r
- try {\r
- deactivate();\r
-\r
- featureSet.clear();\r
- features = NONE;\r
- } catch (ProjectException e1) {\r
- // TODO: do something more sensible than print the possible exception!\r
- e1.printStackTrace();\r
- } finally {\r
- lock.unlock();\r
- }\r
- }\r
-\r
- @Override\r
- public IProjectFeature[] getFeatures() {\r
- assertNotDisposed();\r
-\r
- // Defensive copy.\r
- IProjectFeature[] features = this.features;\r
- return Arrays.copyOf(features, features.length);\r
- }\r
-\r
- public void addFeature(IProjectFeature feature) {\r
- assertNotDisposed();\r
- lock.lock();\r
- try {\r
- if (!featureSet.contains(feature)) {\r
- featureSet.add(feature);\r
- inactiveFeatures.add(feature);\r
- features = featureSet.toArray(new IProjectFeature[featureSet.size()]);\r
- }\r
- } finally {\r
- lock.unlock();\r
- }\r
- }\r
-\r
- @Override\r
- public void activate() throws ProjectException {\r
- if (isDisposed())\r
- throw new IllegalStateException("project is disposed, cannot activate " + this + " (" + System.identityHashCode(this) + ")");\r
-\r
- lock.lock();\r
- try {\r
- if (active.get() == true)\r
- return;\r
-\r
- while (!inactiveFeatures.isEmpty()) {\r
- IProjectFeature feature = inactiveFeatures.getFirst();\r
- boolean success = false;\r
- try {\r
- feature.setProjectElement(this);\r
- feature.configure();\r
-\r
- inactiveFeatures.removeFirst();\r
- activeFeatures.addLast(feature);\r
-\r
- success = true;\r
- } finally {\r
- if (!success)\r
- feature.setProjectElement(null);\r
- }\r
- }\r
-\r
- try {\r
- transactionBarrier();\r
- } finally {\r
- active.set(true);\r
- }\r
- } finally {\r
- lock.unlock();\r
- }\r
- }\r
-\r
- @Override\r
- public void deactivate() throws ProjectException {\r
- if (isDisposed())\r
- throw new IllegalStateException("project is disposed, cannot deactivate " + this + " (" + System.identityHashCode(this) + ")");\r
-\r
- lock.lock();\r
- try {\r
- if (active.get() == false)\r
- return;\r
-\r
- while (!activeFeatures.isEmpty()) {\r
- IProjectFeature feature = activeFeatures.getLast();\r
- boolean success = false;\r
- try {\r
- feature.deconfigure();\r
-\r
- activeFeatures.removeLast();\r
- inactiveFeatures.addFirst(feature);\r
-\r
- success = true;\r
- } finally {\r
- if (success)\r
- feature.setProjectElement(null);\r
- }\r
- }\r
-\r
- try {\r
- transactionBarrier();\r
- } finally {\r
- active.set(false);\r
- }\r
- } finally {\r
- lock.unlock();\r
- }\r
- }\r
-\r
- private void transactionBarrier() throws ProjectException {\r
- // IMPORTANT: ensure that all database requests have been\r
- // completed before returning from deactivation.\r
- try {\r
- session.syncRequest(new WriteOnlyRequest() {\r
- @Override\r
- public void perform(WriteOnlyGraph graph) throws DatabaseException {\r
- throw new CancelTransactionException("barrier");\r
- }\r
- });\r
- } catch (CancelTransactionException e) {\r
- } catch (DatabaseException e) {\r
- throw new ProjectException(e);\r
- }\r
- }\r
-\r
- @Override\r
- public String toString() {\r
- return getClass().getSimpleName() + " [resource=" + get().getResourceId() + "]";\r
- }\r
-\r
- @Override\r
- public int hashCode() {\r
- return get().hashCode();\r
- }\r
-\r
- @Override\r
- public boolean equals(Object obj) {\r
- if (this == obj)\r
- return true;\r
- if (!(obj instanceof Project))\r
- return false;\r
- final Project other = (Project) obj;\r
-\r
- // Need to make sure that the resources are from the same session.\r
- if (!session.equals(other.session))\r
- return false;\r
-\r
- Resource r1 = get();\r
- Resource r2 = other.get();\r
- if (r1 == null) {\r
- if (r2 != null)\r
- return false;\r
- } else if (!r1.equals(r2))\r
- return false;\r
-\r
- return true;\r
- }\r
-\r
- @Override\r
- public Session getSession() {\r
- return session;\r
- }\r
-\r
- @Override\r
- public Resource get() {\r
- return resource;\r
- }\r
-\r
- public RequestProcessor getGraphRequestProcessor() {\r
- MergingGraphRequestProcessor mgrp = session.peekService(MergingGraphRequestProcessor.class);\r
- return mgrp != null ? mgrp : session;\r
- }\r
-\r
- // IDisposable implementation (copied from AbstractDisposable)\r
-\r
- SyncListenerList<IDisposeListener> disposeListeners = null;\r
-\r
- private volatile DisposeState disposeStatus = DisposeState.Alive;\r
-\r
- protected void assertNotDisposed() {\r
- if (isDisposed())\r
- throw new IllegalStateException(this + " is disposed");\r
- }\r
-\r
- @Override\r
- public DisposeState getDisposeState() {\r
- return disposeStatus;\r
- }\r
-\r
- @Override\r
- public boolean isDisposed() {\r
- return disposeStatus == DisposeState.Disposed;\r
- }\r
-\r
- public boolean isAlive() {\r
- return disposeStatus == DisposeState.Alive;\r
- }\r
-\r
- @Override\r
- public void dispose() {\r
- try {\r
- lock.lock();\r
- try {\r
- if (disposeStatus == DisposeState.Disposing)\r
- return;\r
- assertNotDisposed();\r
- disposeStatus = DisposeState.Disposing;\r
- } finally {\r
- lock.unlock();\r
- }\r
-\r
- try {\r
- fireDisposed();\r
- } finally {\r
- doDispose();\r
- }\r
- } finally {\r
- disposeStatus = DisposeState.Disposed;\r
- }\r
- }\r
-\r
- /**\r
- * Disposes if not disposed\r
- */\r
- @Override\r
- public void safeDispose() {\r
- try {\r
- lock.lock();\r
- try {\r
- if (disposeStatus != DisposeState.Alive)\r
- return;\r
- disposeStatus = DisposeState.Disposing;\r
- } finally {\r
- lock.unlock();\r
- }\r
-\r
- try {\r
- fireDisposed();\r
- } finally {\r
- doDispose();\r
- }\r
- } finally {\r
- disposeStatus = DisposeState.Disposed;\r
- }\r
- }\r
-\r
- protected boolean hasDisposeListeners() {\r
- return disposeListeners!=null && !disposeListeners.isEmpty();\r
- }\r
-\r
- private final static Method onDisposed = SyncListenerList.getMethod(IDisposeListener.class, "onDisposed");\r
-\r
- private void fireDisposed() {\r
- if (disposeListeners==null) return;\r
- disposeListeners.fireEventSync(onDisposed, this);\r
- }\r
-\r
- @SuppressWarnings("unused")\r
- private void fireDisposedAsync() {\r
- if (disposeListeners==null) return;\r
- disposeListeners.fireEventAsync(onDisposed, this);\r
- }\r
-\r
- @Override\r
- public void addDisposeListener(IDisposeListener listener) {\r
- lazyGetListenerList().add(listener);\r
- }\r
-\r
- @Override\r
- public void addDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {\r
- lazyGetListenerList().add(thread, listener);\r
- }\r
-\r
- @Override\r
- public void removeDisposeListener(IDisposeListener listener) {\r
- if (disposeListeners==null) return;\r
- disposeListeners.remove(listener);\r
- }\r
-\r
- @Override\r
- public void removeDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {\r
- if (disposeListeners==null) return;\r
- disposeListeners.remove(thread, listener);\r
- }\r
-\r
- private synchronized SyncListenerList<IDisposeListener> lazyGetListenerList() {\r
- if (disposeListeners==null)\r
- disposeListeners = new SyncListenerList<IDisposeListener>(IDisposeListener.class);\r
- return disposeListeners;\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.project.impl;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.simantics.db.RequestProcessor;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.WriteOnlyGraph;
+import org.simantics.db.common.processor.MergingGraphRequestProcessor;
+import org.simantics.db.common.request.WriteOnlyRequest;
+import org.simantics.db.exception.CancelTransactionException;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.project.IProject;
+import org.simantics.project.exception.ProjectException;
+import org.simantics.project.features.IProjectFeature;
+import org.simantics.utils.datastructures.disposable.DisposeState;
+import org.simantics.utils.datastructures.disposable.IDisposeListener;
+import org.simantics.utils.datastructures.hints.HintContext;
+import org.simantics.utils.threads.IThreadWorkQueue;
+import org.simantics.utils.threads.SyncListenerList;
+
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public class Project extends HintContext implements IProject {
+
+ private static IProjectFeature[] NONE = {};
+
+ //private static final String TAG_PROJECTS = "projects";
+
+ private IProjectFeature[] features = NONE;
+ private final LinkedList<IProjectFeature> featureSet = new LinkedList<IProjectFeature>();
+ private final LinkedList<IProjectFeature> inactiveFeatures = new LinkedList<IProjectFeature>();
+ private final LinkedList<IProjectFeature> activeFeatures = new LinkedList<IProjectFeature>();
+
+ private final Lock lock = new ReentrantLock();
+ private final AtomicBoolean active = new AtomicBoolean(false);
+
+ protected Session session;
+ protected Resource resource;
+
+ public Project(Session session, Resource resource) {
+ if (session == null)
+ throw new NullPointerException("null session");
+ if (resource == null)
+ throw new NullPointerException("null resource");
+
+ this.session = session;
+ this.resource = resource;
+ //System.out.println("NEW PROJECT [" + resource.getResourceId() + "] (" + System.identityHashCode(this) + ")");
+ }
+
+ public void doDispose() {
+ //System.out.println("DISPOSE: " + this + " (" + System.identityHashCode(this) + ")");
+ lock.lock();
+ try {
+ deactivate();
+
+ featureSet.clear();
+ features = NONE;
+ } catch (ProjectException e1) {
+ // TODO: do something more sensible than print the possible exception!
+ e1.printStackTrace();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public IProjectFeature[] getFeatures() {
+ assertNotDisposed();
+
+ // Defensive copy.
+ IProjectFeature[] features = this.features;
+ return Arrays.copyOf(features, features.length);
+ }
+
+ public void addFeature(IProjectFeature feature) {
+ assertNotDisposed();
+ lock.lock();
+ try {
+ if (!featureSet.contains(feature)) {
+ featureSet.add(feature);
+ inactiveFeatures.add(feature);
+ features = featureSet.toArray(new IProjectFeature[featureSet.size()]);
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public void activate() throws ProjectException {
+ if (isDisposed())
+ throw new IllegalStateException("project is disposed, cannot activate " + this + " (" + System.identityHashCode(this) + ")");
+
+ lock.lock();
+ try {
+ if (active.get() == true)
+ return;
+
+ while (!inactiveFeatures.isEmpty()) {
+ IProjectFeature feature = inactiveFeatures.getFirst();
+ boolean success = false;
+ try {
+ feature.setProjectElement(this);
+ feature.configure();
+
+ inactiveFeatures.removeFirst();
+ activeFeatures.addLast(feature);
+
+ success = true;
+ } finally {
+ if (!success)
+ feature.setProjectElement(null);
+ }
+ }
+
+ try {
+ transactionBarrier();
+ } finally {
+ active.set(true);
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ @Override
+ public void deactivate() throws ProjectException {
+ if (isDisposed())
+ throw new IllegalStateException("project is disposed, cannot deactivate " + this + " (" + System.identityHashCode(this) + ")");
+
+ lock.lock();
+ try {
+ if (active.get() == false)
+ return;
+
+ while (!activeFeatures.isEmpty()) {
+ IProjectFeature feature = activeFeatures.getLast();
+ boolean success = false;
+ try {
+ feature.deconfigure();
+
+ activeFeatures.removeLast();
+ inactiveFeatures.addFirst(feature);
+
+ success = true;
+ } finally {
+ if (success)
+ feature.setProjectElement(null);
+ }
+ }
+
+ try {
+ transactionBarrier();
+ } finally {
+ active.set(false);
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private void transactionBarrier() throws ProjectException {
+ // IMPORTANT: ensure that all database requests have been
+ // completed before returning from deactivation.
+ try {
+ session.syncRequest(new WriteOnlyRequest() {
+ @Override
+ public void perform(WriteOnlyGraph graph) throws DatabaseException {
+ throw new CancelTransactionException("barrier");
+ }
+ });
+ } catch (CancelTransactionException e) {
+ } catch (DatabaseException e) {
+ throw new ProjectException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + " [resource=" + get().getResourceId() + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ return get().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof Project))
+ return false;
+ final Project other = (Project) obj;
+
+ // Need to make sure that the resources are from the same session.
+ if (!session.equals(other.session))
+ return false;
+
+ Resource r1 = get();
+ Resource r2 = other.get();
+ if (r1 == null) {
+ if (r2 != null)
+ return false;
+ } else if (!r1.equals(r2))
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public Session getSession() {
+ return session;
+ }
+
+ @Override
+ public Resource get() {
+ return resource;
+ }
+
+ public RequestProcessor getGraphRequestProcessor() {
+ MergingGraphRequestProcessor mgrp = session.peekService(MergingGraphRequestProcessor.class);
+ return mgrp != null ? mgrp : session;
+ }
+
+ // IDisposable implementation (copied from AbstractDisposable)
+
+ SyncListenerList<IDisposeListener> disposeListeners = null;
+
+ private volatile DisposeState disposeStatus = DisposeState.Alive;
+
+ protected void assertNotDisposed() {
+ if (isDisposed())
+ throw new IllegalStateException(this + " is disposed");
+ }
+
+ @Override
+ public DisposeState getDisposeState() {
+ return disposeStatus;
+ }
+
+ @Override
+ public boolean isDisposed() {
+ return disposeStatus == DisposeState.Disposed;
+ }
+
+ public boolean isAlive() {
+ return disposeStatus == DisposeState.Alive;
+ }
+
+ @Override
+ public void dispose() {
+ try {
+ lock.lock();
+ try {
+ if (disposeStatus == DisposeState.Disposing)
+ return;
+ assertNotDisposed();
+ disposeStatus = DisposeState.Disposing;
+ } finally {
+ lock.unlock();
+ }
+
+ try {
+ fireDisposed();
+ } finally {
+ doDispose();
+ }
+ } finally {
+ disposeStatus = DisposeState.Disposed;
+ }
+ }
+
+ /**
+ * Disposes if not disposed
+ */
+ @Override
+ public void safeDispose() {
+ try {
+ lock.lock();
+ try {
+ if (disposeStatus != DisposeState.Alive)
+ return;
+ disposeStatus = DisposeState.Disposing;
+ } finally {
+ lock.unlock();
+ }
+
+ try {
+ fireDisposed();
+ } finally {
+ doDispose();
+ }
+ } finally {
+ disposeStatus = DisposeState.Disposed;
+ }
+ }
+
+ protected boolean hasDisposeListeners() {
+ return disposeListeners!=null && !disposeListeners.isEmpty();
+ }
+
+ private final static Method onDisposed = SyncListenerList.getMethod(IDisposeListener.class, "onDisposed");
+
+ private void fireDisposed() {
+ if (disposeListeners==null) return;
+ disposeListeners.fireEventSync(onDisposed, this);
+ }
+
+ @SuppressWarnings("unused")
+ private void fireDisposedAsync() {
+ if (disposeListeners==null) return;
+ disposeListeners.fireEventAsync(onDisposed, this);
+ }
+
+ @Override
+ public void addDisposeListener(IDisposeListener listener) {
+ lazyGetListenerList().add(listener);
+ }
+
+ @Override
+ public void addDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
+ lazyGetListenerList().add(thread, listener);
+ }
+
+ @Override
+ public void removeDisposeListener(IDisposeListener listener) {
+ if (disposeListeners==null) return;
+ disposeListeners.remove(listener);
+ }
+
+ @Override
+ public void removeDisposeListener(IDisposeListener listener, IThreadWorkQueue thread) {
+ if (disposeListeners==null) return;
+ disposeListeners.remove(thread, listener);
+ }
+
+ private synchronized SyncListenerList<IDisposeListener> lazyGetListenerList() {
+ if (disposeListeners==null)
+ disposeListeners = new SyncListenerList<IDisposeListener>(IDisposeListener.class);
+ return disposeListeners;
+ }
+
+}