-/*******************************************************************************\r
- * Copyright (c) 2007, 2011 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.db.management;\r
-\r
-import java.io.IOException;\r
-import java.lang.reflect.Method;\r
-import java.util.Arrays;\r
-import java.util.UUID;\r
-import java.util.concurrent.TimeoutException;\r
-\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.simantics.db.Disposable;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.ServerReference;\r
-import org.simantics.db.Session;\r
-import org.simantics.db.SessionManager;\r
-import org.simantics.db.SessionReference;\r
-import org.simantics.db.VirtualGraph;\r
-import org.simantics.db.authentication.UserAuthenticationAgent;\r
-import org.simantics.db.common.processor.MergingDelayedWriteProcessor;\r
-import org.simantics.db.common.processor.MergingGraphRequestProcessor;\r
-import org.simantics.db.common.request.ReadRequest;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.exception.InternalException;\r
-import org.simantics.db.management.internal.Activator;\r
-import org.simantics.db.service.LifecycleSupport;\r
-import org.simantics.db.service.VirtualGraphSupport;\r
-import org.simantics.db.services.GlobalServiceInitializer;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.utils.datastructures.disposable.DisposeState;\r
-import org.simantics.utils.datastructures.disposable.IDisposable;\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
-import fi.vtt.simantics.procore.ProCoreServerReference;\r
-import fi.vtt.simantics.procore.ProCoreSessionReference;\r
-import fi.vtt.simantics.procore.SessionManagerSource;\r
-\r
-/**\r
- * Holds all information that is needed to create and manage a single database\r
- * session in the Simantics workbench UI.\r
- *\r
- * @see org.simantics.ui.SimanticsUI\r
- * @see org.simantics.db.layer0.util.Simantics\r
- * @author Tuukka Lehtonen\r
- */\r
-public class SessionContext extends HintContext implements ISessionContext, Disposable {\r
- private static final boolean SESSION_DEBUG = false;\r
-\r
-// private final IServerAddress address;\r
-\r
- private Session session;\r
-\r
- private UserAuthenticationAgent authenticator;\r
-\r
- private boolean servicesRegistered = false;\r
-\r
- private IStatus servicesRegisteredStatus = null;\r
-\r
- public static SessionContext create(Session session, boolean initialize) throws DatabaseException {\r
- return new SessionContext(session, initialize);\r
- }\r
-\r
-// public static SessionContext openSession(IServerAddress info, UserAuthenticationAgent auth) throws IOException, DatabaseException {\r
-// return new SessionContext(info, auth, false);\r
-// }\r
-//\r
-// public static SessionContext openAndInitializeSession(IServerAddress info, UserAuthenticationAgent auth) throws IOException, DatabaseException {\r
-// return new SessionContext(info, auth, true);\r
-// }\r
-\r
- private static SessionReference createSessionReference(/*IServerAddress address,*/ long sessionId) throws InternalException {\r
- ProCoreServerReference server = new ProCoreServerReference();\r
- ProCoreSessionReference ref = new ProCoreSessionReference(server, sessionId);\r
- return ref;\r
- }\r
-\r
-// private SessionContext(IServerAddress addr, UserAuthenticationAgent auth, boolean initialize) throws IOException, DatabaseException {\r
-// if (addr == null)\r
-// throw new IllegalArgumentException("null address");\r
-//// this.address = addr;\r
-// this.authenticator = auth;\r
-//\r
-// SessionManager sessionManager = SessionManagerProvider.getInstance().getSessionManager();\r
-//\r
-// if (initialize) {\r
-// initializeSession(sessionManager);\r
-// if (SESSION_DEBUG) {\r
-// System.err.println("Initialized session: " + addr);\r
-// System.err.flush();\r
-// }\r
-// } else {\r
-// SessionReference ref = createSessionReference(SessionManagerSource.NullSessionId);\r
-// this.session = sessionManager.createSession(ref, auth);\r
-// if (SESSION_DEBUG) {\r
-// System.err.println("Opened session: " + addr);\r
-// System.err.flush();\r
-// }\r
-// }\r
-//\r
-// }\r
-\r
- private SessionContext(Session session, boolean initialize) throws DatabaseException {\r
- if (initialize)\r
- initializeSession(session);\r
- ServerReference ref = session.getService( LifecycleSupport.class ).getSessionReference().getServerReference();\r
-// this.address = ref.serverAddress();\r
- this.session = session;\r
- }\r
-\r
- private void initializeSession(SessionManager sessionManager) throws DatabaseException, IOException {\r
- Session s = null;\r
- boolean success = false;\r
- try {\r
- SessionReference ref = createSessionReference(SessionManagerSource.NullSessionId);\r
- s = sessionManager.createSession(ref, authenticator);\r
- initializeSession(s);\r
- this.session = s;\r
- success = true;\r
- } finally {\r
- if (!success) {\r
- if (s != null) {\r
- LifecycleSupport support = s.getService(LifecycleSupport.class);\r
- support.close();\r
- }\r
- }\r
- }\r
- }\r
-\r
- private void initializeSession(Session s) throws DatabaseException {\r
- s.registerService(MergingGraphRequestProcessor.class, new MergingGraphRequestProcessor("SessionService", s, 20));\r
- s.registerService(MergingDelayedWriteProcessor.class, new MergingDelayedWriteProcessor(s, 20));\r
- s.registerService(VirtualGraph.class, s.getService(VirtualGraphSupport.class).getMemoryPersistent(UUID.randomUUID().toString()));\r
-\r
- // Builtins needs to be initialized for the new session before\r
- // anything useful can be done with it.\r
- s.syncRequest(new ReadRequest() {\r
- @Override\r
- public void run(ReadGraph g) {\r
- // Registers Builtins with the ServiceLocator of the Graph's session.\r
- Layer0.getInstance(g);\r
- }\r
- });\r
- }\r
-\r
- public void registerServices() {\r
- if (servicesRegistered)\r
- return;\r
-\r
- // Register any services available for the SessionLocator of the new\r
- // Session.\r
- servicesRegisteredStatus = new GlobalServiceInitializer().initialize(session);\r
- if (!servicesRegisteredStatus.isOK()) {\r
- Activator.getDefault().getLog().log(servicesRegisteredStatus);\r
- }\r
-\r
- servicesRegistered = true;\r
- }\r
-\r
-// @Override\r
-// public IServerAddress getAddress() {\r
-// return address;\r
-// }\r
-\r
- @Override\r
- public Session getSession() {\r
- if (session == null)\r
- throw new IllegalStateException("SessionContext is disposed");\r
- return session;\r
- }\r
-\r
- @Override\r
- public Session peekSession() {\r
- return session;\r
- }\r
-\r
- /**\r
- * Do dispose procedures. This method is invoked at most once.\r
- */\r
- protected void doDispose() {\r
- try {\r
- doClose();\r
- } catch (Exception e) {\r
- throw new RuntimeException(e);\r
- }\r
- }\r
-\r
- @Override\r
- public void close() throws IllegalStateException, InterruptedException, TimeoutException {\r
- try {\r
- dispose();\r
- } catch (RuntimeException e) {\r
- Throwable t = e.getCause();\r
- if (t instanceof RuntimeException) {\r
- throw (RuntimeException) t;\r
- } else if (t instanceof Error) {\r
- throw (Error) t;\r
- } else if (t instanceof InterruptedException) {\r
- throw (InterruptedException) t;\r
- } else if (t instanceof TimeoutException) {\r
- throw (TimeoutException) t;\r
- }\r
- throw e;\r
- }\r
- }\r
-\r
- private void doClose() throws DatabaseException {\r
- for (Key k : getHints().keySet()) {\r
- if (k != null) {\r
- Object o = removeHint(k);\r
- if (o instanceof IDisposable) {\r
- ((IDisposable) o).safeDispose();\r
- }\r
- }\r
- }\r
-\r
- if (session != null) {\r
- if (SESSION_DEBUG) {\r
- System.err.println("Closing session: " + session/*address*/);\r
- System.err.flush();\r
- }\r
- LifecycleSupport support = session.getService(LifecycleSupport.class);\r
- support.close(0, true);\r
- session = null;\r
- }\r
- }\r
-\r
- @Override\r
- public int hashCode() {\r
- if (session == null) return 0;\r
- return session.hashCode();\r
- }\r
-\r
- @Override\r
- public boolean equals(Object obj) {\r
- if (this == obj)\r
- return true;\r
- if (obj == null || getClass() != obj.getClass())\r
- return false;\r
- final SessionContext other = (SessionContext) obj;\r
-// if (!address.equals(other.address))\r
-// return false;\r
- return session.equals(other.session);\r
- }\r
-\r
- @Override\r
- public String toString() {\r
- StringBuilder s = new StringBuilder();\r
- s.append("SessionContext [info=" + session + ", hints=");\r
- s.append(Arrays.toString(getHints().values().toArray()));\r
- s.append("]");\r
- return s.toString();\r
- }\r
-\r
-\r
- // IDisposable implementation (AbstractDisposable)\r
-\r
- SyncListenerList<IDisposeListener> disposeListeners = null;\r
-\r
- private DisposeState disposeStatus = DisposeState.Alive;\r
-\r
- protected void assertNotDisposed() {\r
- if (isDisposed())\r
- throw new AssertionError(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
- synchronized (this) {\r
- if (disposeStatus == DisposeState.Disposing)\r
- return;\r
- assertNotDisposed();\r
- disposeStatus = DisposeState.Disposing;\r
- }\r
- try {\r
- fireDisposed();\r
- } finally {\r
- doDispose();\r
- }\r
- } finally {\r
- synchronized(this) {\r
- disposeStatus = DisposeState.Disposed;\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Disposes if not disposed\r
- */\r
- @Override\r
- public void safeDispose() {\r
- try {\r
- synchronized (this) {\r
- if (disposeStatus != DisposeState.Alive)\r
- return;\r
- disposeStatus = DisposeState.Disposing;\r
- }\r
- try {\r
- fireDisposed();\r
- } finally {\r
- doDispose();\r
- }\r
- } finally {\r
- synchronized(this) {\r
- disposeStatus = DisposeState.Disposed;\r
- }\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
- {\r
- if (disposeListeners==null)\r
- disposeListeners = new SyncListenerList<IDisposeListener>(IDisposeListener.class);\r
- return disposeListeners;\r
- }\r
-\r
- @Override\r
- protected void finalize() throws Throwable {\r
- try {\r
- safeDispose();\r
- } finally {\r
- super.finalize();\r
- }\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 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.db.management;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.UUID;
+import java.util.concurrent.TimeoutException;
+
+import org.eclipse.core.runtime.IStatus;
+import org.simantics.db.Disposable;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.ServerReference;
+import org.simantics.db.Session;
+import org.simantics.db.SessionManager;
+import org.simantics.db.SessionReference;
+import org.simantics.db.VirtualGraph;
+import org.simantics.db.authentication.UserAuthenticationAgent;
+import org.simantics.db.common.processor.MergingDelayedWriteProcessor;
+import org.simantics.db.common.processor.MergingGraphRequestProcessor;
+import org.simantics.db.common.request.ReadRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.InternalException;
+import org.simantics.db.management.internal.Activator;
+import org.simantics.db.service.LifecycleSupport;
+import org.simantics.db.service.VirtualGraphSupport;
+import org.simantics.db.services.GlobalServiceInitializer;
+import org.simantics.layer0.Layer0;
+import org.simantics.utils.datastructures.disposable.DisposeState;
+import org.simantics.utils.datastructures.disposable.IDisposable;
+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;
+
+import fi.vtt.simantics.procore.ProCoreServerReference;
+import fi.vtt.simantics.procore.ProCoreSessionReference;
+import fi.vtt.simantics.procore.SessionManagerSource;
+
+/**
+ * Holds all information that is needed to create and manage a single database
+ * session in the Simantics workbench UI.
+ *
+ * @see org.simantics.ui.SimanticsUI
+ * @see org.simantics.db.layer0.util.Simantics
+ * @author Tuukka Lehtonen
+ */
+public class SessionContext extends HintContext implements ISessionContext, Disposable {
+ private static final boolean SESSION_DEBUG = false;
+
+// private final IServerAddress address;
+
+ private Session session;
+
+ private UserAuthenticationAgent authenticator;
+
+ private boolean servicesRegistered = false;
+
+ private IStatus servicesRegisteredStatus = null;
+
+ public static SessionContext create(Session session, boolean initialize) throws DatabaseException {
+ return new SessionContext(session, initialize);
+ }
+
+// public static SessionContext openSession(IServerAddress info, UserAuthenticationAgent auth) throws IOException, DatabaseException {
+// return new SessionContext(info, auth, false);
+// }
+//
+// public static SessionContext openAndInitializeSession(IServerAddress info, UserAuthenticationAgent auth) throws IOException, DatabaseException {
+// return new SessionContext(info, auth, true);
+// }
+
+ private static SessionReference createSessionReference(/*IServerAddress address,*/ long sessionId) throws InternalException {
+ ProCoreServerReference server = new ProCoreServerReference();
+ ProCoreSessionReference ref = new ProCoreSessionReference(server, sessionId);
+ return ref;
+ }
+
+// private SessionContext(IServerAddress addr, UserAuthenticationAgent auth, boolean initialize) throws IOException, DatabaseException {
+// if (addr == null)
+// throw new IllegalArgumentException("null address");
+//// this.address = addr;
+// this.authenticator = auth;
+//
+// SessionManager sessionManager = SessionManagerProvider.getInstance().getSessionManager();
+//
+// if (initialize) {
+// initializeSession(sessionManager);
+// if (SESSION_DEBUG) {
+// System.err.println("Initialized session: " + addr);
+// System.err.flush();
+// }
+// } else {
+// SessionReference ref = createSessionReference(SessionManagerSource.NullSessionId);
+// this.session = sessionManager.createSession(ref, auth);
+// if (SESSION_DEBUG) {
+// System.err.println("Opened session: " + addr);
+// System.err.flush();
+// }
+// }
+//
+// }
+
+ private SessionContext(Session session, boolean initialize) throws DatabaseException {
+ if (initialize)
+ initializeSession(session);
+ ServerReference ref = session.getService( LifecycleSupport.class ).getSessionReference().getServerReference();
+// this.address = ref.serverAddress();
+ this.session = session;
+ }
+
+ private void initializeSession(SessionManager sessionManager) throws DatabaseException, IOException {
+ Session s = null;
+ boolean success = false;
+ try {
+ SessionReference ref = createSessionReference(SessionManagerSource.NullSessionId);
+ s = sessionManager.createSession(ref, authenticator);
+ initializeSession(s);
+ this.session = s;
+ success = true;
+ } finally {
+ if (!success) {
+ if (s != null) {
+ LifecycleSupport support = s.getService(LifecycleSupport.class);
+ support.close();
+ }
+ }
+ }
+ }
+
+ private void initializeSession(Session s) throws DatabaseException {
+ s.registerService(MergingGraphRequestProcessor.class, new MergingGraphRequestProcessor("SessionService", s, 20));
+ s.registerService(MergingDelayedWriteProcessor.class, new MergingDelayedWriteProcessor(s, 20));
+ s.registerService(VirtualGraph.class, s.getService(VirtualGraphSupport.class).getMemoryPersistent(UUID.randomUUID().toString()));
+
+ // Builtins needs to be initialized for the new session before
+ // anything useful can be done with it.
+ s.syncRequest(new ReadRequest() {
+ @Override
+ public void run(ReadGraph g) {
+ // Registers Builtins with the ServiceLocator of the Graph's session.
+ Layer0.getInstance(g);
+ }
+ });
+ }
+
+ public void registerServices() {
+ if (servicesRegistered)
+ return;
+
+ // Register any services available for the SessionLocator of the new
+ // Session.
+ servicesRegisteredStatus = new GlobalServiceInitializer().initialize(session);
+ if (!servicesRegisteredStatus.isOK()) {
+ Activator.getDefault().getLog().log(servicesRegisteredStatus);
+ }
+
+ servicesRegistered = true;
+ }
+
+// @Override
+// public IServerAddress getAddress() {
+// return address;
+// }
+
+ @Override
+ public Session getSession() {
+ if (session == null)
+ throw new IllegalStateException("SessionContext is disposed");
+ return session;
+ }
+
+ @Override
+ public Session peekSession() {
+ return session;
+ }
+
+ /**
+ * Do dispose procedures. This method is invoked at most once.
+ */
+ protected void doDispose() {
+ try {
+ doClose();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void close() throws IllegalStateException, InterruptedException, TimeoutException {
+ try {
+ dispose();
+ } catch (RuntimeException e) {
+ Throwable t = e.getCause();
+ if (t instanceof RuntimeException) {
+ throw (RuntimeException) t;
+ } else if (t instanceof Error) {
+ throw (Error) t;
+ } else if (t instanceof InterruptedException) {
+ throw (InterruptedException) t;
+ } else if (t instanceof TimeoutException) {
+ throw (TimeoutException) t;
+ }
+ throw e;
+ }
+ }
+
+ private void doClose() throws DatabaseException {
+ for (Key k : getHints().keySet()) {
+ if (k != null) {
+ Object o = removeHint(k);
+ if (o instanceof IDisposable) {
+ ((IDisposable) o).safeDispose();
+ }
+ }
+ }
+
+ if (session != null) {
+ if (SESSION_DEBUG) {
+ System.err.println("Closing session: " + session/*address*/);
+ System.err.flush();
+ }
+ LifecycleSupport support = session.getService(LifecycleSupport.class);
+ support.close(0, true);
+ session = null;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ if (session == null) return 0;
+ return session.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null || getClass() != obj.getClass())
+ return false;
+ final SessionContext other = (SessionContext) obj;
+// if (!address.equals(other.address))
+// return false;
+ return session.equals(other.session);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("SessionContext [info=" + session + ", hints=");
+ s.append(Arrays.toString(getHints().values().toArray()));
+ s.append("]");
+ return s.toString();
+ }
+
+
+ // IDisposable implementation (AbstractDisposable)
+
+ SyncListenerList<IDisposeListener> disposeListeners = null;
+
+ private DisposeState disposeStatus = DisposeState.Alive;
+
+ protected void assertNotDisposed() {
+ if (isDisposed())
+ throw new AssertionError(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 {
+ synchronized (this) {
+ if (disposeStatus == DisposeState.Disposing)
+ return;
+ assertNotDisposed();
+ disposeStatus = DisposeState.Disposing;
+ }
+ try {
+ fireDisposed();
+ } finally {
+ doDispose();
+ }
+ } finally {
+ synchronized(this) {
+ disposeStatus = DisposeState.Disposed;
+ }
+ }
+ }
+
+ /**
+ * Disposes if not disposed
+ */
+ @Override
+ public void safeDispose() {
+ try {
+ synchronized (this) {
+ if (disposeStatus != DisposeState.Alive)
+ return;
+ disposeStatus = DisposeState.Disposing;
+ }
+ try {
+ fireDisposed();
+ } finally {
+ doDispose();
+ }
+ } finally {
+ synchronized(this) {
+ 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;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ safeDispose();
+ } finally {
+ super.finalize();
+ }
+ }
+
+}