X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=bundles%2Forg.simantics.db.layer0%2Fsrc%2Forg%2Fsimantics%2Fdb%2Flayer0%2Futil%2FEvaluatingListener.java;fp=bundles%2Forg.simantics.db.layer0%2Fsrc%2Forg%2Fsimantics%2Fdb%2Flayer0%2Futil%2FEvaluatingListener.java;h=59dcc5910e2e018402945007d0bd28ab8d24519b;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git
diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/util/EvaluatingListener.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/util/EvaluatingListener.java
new file mode 100644
index 000000000..59dcc5910
--- /dev/null
+++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/util/EvaluatingListener.java
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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.layer0.util;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.simantics.db.Session;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.procedure.Listener;
+import org.simantics.db.request.Read;
+
+/**
+ * A database listener that disposes itself based on a criterion evaluated using
+ * the request results received by the listener. If the results pass the
+ * criterion, the listener is disposed and the result is either accepted or
+ * discarded based on the evaluation.
+ *
+ *
+ * The reason for the existence of this class is that sometimes database
+ * requests always perform asynchronously, i.e. return empty results at first
+ * and later get updated to their final results. For example, a the results of a
+ * web request would take a while to be delivered. This listener makes it a bit
+ * easier to deal with these situations when results are needed synchronously.
+ *
+ *
+ * {@link #trySyncRequest(Session, Read, Criterion, long, TimeUnit)} provides a
+ * utility for executing a synchronous read request with this listener so that
+ * the implementation will wait for a criterion accepted result for the
+ * specified amount of time. If an accepted result is reached within the time
+ * limit, it will be returned. If the time limit is reached and no accepted
+ * result is attained, null
will be returned. If the request
+ * produces an exception, it will be thrown.
+ *
+ * @author Tuukka Lehtonen
+ *
+ * @param database request result type
+ *
+ * @see EvaluatingListener.Evaluation
+ * @see EvaluatingListener.Criterion
+ */
+public class EvaluatingListener implements Listener {
+
+ public static enum Evaluation {
+ /**
+ * Keep on listening to further results.
+ */
+ IGNORE,
+ /**
+ * Dispose listener and discard the results.
+ */
+ DISCARD,
+ /**
+ * Dispose listener and return the latest result.
+ */
+ ACCEPT
+ }
+
+ /**
+ * An evaluable criterion for the result received by
+ * {@link Listener#execute(Object)} to tell whether to accept the result,
+ * wait for another result or to consider the listener disposed.
+ *
+ * @param the type of the result
+ */
+ public static interface Criterion {
+ Evaluation evaluate(T result);
+ }
+
+ /**
+ * The criterion the listener evaluates. When it evaluates to
+ * {@value Evaluation#DISCARD}, this field is nullified and the listener is
+ * considered disposed.
+ */
+ private volatile Criterion criterion;
+ private T result;
+ private Throwable exception;
+ private Semaphore wait = new Semaphore(0);
+
+ public EvaluatingListener(Criterion criterion) {
+ if (criterion == null)
+ throw new NullPointerException("null criterion");
+ this.criterion = criterion;
+ }
+
+ /**
+ * @param session
+ * @param request
+ * @param criterion
+ * @param timeout
+ * @param unit
+ * @return
+ * @throws InterruptedException
+ * @throws DatabaseException
+ */
+ public static T trySyncRequest(Session session, Read request, Criterion criterion, long timeout, TimeUnit unit) throws InterruptedException, DatabaseException {
+ EvaluatingListener l = new EvaluatingListener(criterion);
+ session.asyncRequest(request, l);
+ l.tryWaitForResult(timeout, unit);
+ // Make sure the listener is disposed.
+ l.dispose();
+ l.throwPossibleException();
+ return l.getResult();
+ }
+
+ public T waitForResult() throws InterruptedException {
+ wait.acquire();
+ return getResult();
+ }
+
+ public boolean tryWaitForResult(long timeout, TimeUnit unit) throws InterruptedException {
+ return wait.tryAcquire(timeout, unit);
+ }
+
+ public T getResult() {
+ return result;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public void throwPossibleException() throws DatabaseException {
+ if (exception != null) {
+ if (exception instanceof DatabaseException)
+ throw (DatabaseException) exception;
+ throw new DatabaseException(exception);
+ }
+ }
+
+ @Override
+ public void execute(T result) {
+ Criterion crit = criterion;
+ if (crit == null)
+ return;
+ EvaluatingListener.Evaluation e = crit.evaluate(result);
+ switch (e) {
+ case IGNORE:
+ ignored(result);
+ return;
+ case ACCEPT:
+ this.result = result;
+ try {
+ accepted(result);
+ } finally {
+ dispose();
+ wait.release();
+ }
+ return;
+ case DISCARD:
+ dispose();
+ wait.release();
+ return;
+ }
+ }
+
+ /**
+ * Override to process results that were ignored.
+ *
+ * @param result
+ */
+ public void ignored(T result) {
+ }
+
+ /**
+ * Override this to immediately process an accepted result in the listener.
+ * This method is invoked before the listener is disposed
+ *
+ * @param result
+ */
+ public void accepted(T result) {
+ }
+
+ @Override
+ public void exception(Throwable t) {
+ this.exception = t;
+ dispose();
+ wait.release();
+ }
+
+ private void dispose() {
+ this.criterion = null;
+ }
+
+ @Override
+ public boolean isDisposed() {
+ return criterion == null;
+ }
+
+}
\ No newline at end of file