1 /*******************************************************************************
2 * Copyright (c) 2012 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.db.layer0.util;
14 import java.util.concurrent.Semaphore;
15 import java.util.concurrent.TimeUnit;
17 import org.simantics.db.Session;
18 import org.simantics.db.exception.DatabaseException;
19 import org.simantics.db.procedure.Listener;
20 import org.simantics.db.request.Read;
23 * A database listener that disposes itself based on a criterion evaluated using
24 * the request results received by the listener. If the results pass the
25 * criterion, the listener is disposed and the result is either accepted or
26 * discarded based on the evaluation.
29 * The reason for the existence of this class is that sometimes database
30 * requests always perform asynchronously, i.e. return empty results at first
31 * and later get updated to their final results. For example, a the results of a
32 * web request would take a while to be delivered. This listener makes it a bit
33 * easier to deal with these situations when results are needed synchronously.
36 * {@link #trySyncRequest(Session, Read, Criterion, long, TimeUnit)} provides a
37 * utility for executing a synchronous read request with this listener so that
38 * the implementation will wait for a criterion accepted result for the
39 * specified amount of time. If an accepted result is reached within the time
40 * limit, it will be returned. If the time limit is reached and no accepted
41 * result is attained, <code>null</code> will be returned. If the request
42 * produces an exception, it will be thrown.
44 * @author Tuukka Lehtonen
46 * @param <T> database request result type
48 * @see EvaluatingListener.Evaluation
49 * @see EvaluatingListener.Criterion
51 public class EvaluatingListener<T> implements Listener<T> {
53 public static enum Evaluation {
55 * Keep on listening to further results.
59 * Dispose listener and discard the results.
63 * Dispose listener and return the latest result.
69 * An evaluable criterion for the result received by
70 * {@link Listener#execute(Object)} to tell whether to accept the result,
71 * wait for another result or to consider the listener disposed.
73 * @param <T> the type of the result
75 public static interface Criterion<T> {
76 Evaluation evaluate(T result);
80 * The criterion the listener evaluates. When it evaluates to
81 * {@value Evaluation#DISCARD}, this field is nullified and the listener is
82 * considered disposed.
84 private volatile Criterion<T> criterion;
86 private Throwable exception;
87 private Semaphore wait = new Semaphore(0);
89 public EvaluatingListener(Criterion<T> criterion) {
90 if (criterion == null)
91 throw new NullPointerException("null criterion");
92 this.criterion = criterion;
102 * @throws InterruptedException
103 * @throws DatabaseException
105 public static <T> T trySyncRequest(Session session, Read<T> request, Criterion<T> criterion, long timeout, TimeUnit unit) throws InterruptedException, DatabaseException {
106 EvaluatingListener<T> l = new EvaluatingListener<T>(criterion);
107 session.asyncRequest(request, l);
108 l.tryWaitForResult(timeout, unit);
109 // Make sure the listener is disposed.
111 l.throwPossibleException();
112 return l.getResult();
115 public T waitForResult() throws InterruptedException {
120 public boolean tryWaitForResult(long timeout, TimeUnit unit) throws InterruptedException {
121 return wait.tryAcquire(timeout, unit);
124 public T getResult() {
128 public Throwable getException() {
132 public void throwPossibleException() throws DatabaseException {
133 if (exception != null) {
134 if (exception instanceof DatabaseException)
135 throw (DatabaseException) exception;
136 throw new DatabaseException(exception);
141 public void execute(T result) {
142 Criterion<T> crit = criterion;
145 EvaluatingListener.Evaluation e = crit.evaluate(result);
151 this.result = result;
167 * Override to process results that were ignored.
171 public void ignored(T result) {
175 * Override this to immediately process an accepted result in the listener.
176 * This method is invoked before the listener is disposed
180 public void accepted(T result) {
184 public void exception(Throwable t) {
190 private void dispose() {
191 this.criterion = null;
195 public boolean isDisposed() {
196 return criterion == null;