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