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