/******************************************************************************* * 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.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.Session; import org.simantics.db.VirtualGraph; 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.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; /** * 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 boolean servicesRegistered = false; private IStatus servicesRegisteredStatus = null; public static SessionContext create(Session session, boolean initialize) throws DatabaseException { return new SessionContext(session, initialize); } private SessionContext(Session session, boolean initialize) throws DatabaseException { if (initialize) initializeSession(session); this.session = session; } 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 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 lazyGetListenerList() { if (disposeListeners==null) disposeListeners = new SyncListenerList(IDisposeListener.class); return disposeListeners; } @Override protected void finalize() throws Throwable { try { safeDispose(); } finally { super.finalize(); } } }