]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/util/EvaluatingListener.java
Sync git svn branch with SVN repository r33158.
[simantics/platform.git] / bundles / org.simantics.db.layer0 / src / org / simantics / db / layer0 / util / EvaluatingListener.java
1 /*******************************************************************************\r
2  * Copyright (c) 2012 Association for Decentralized Information Management in\r
3  * Industry THTH ry.\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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.db.layer0.util;\r
13 \r
14 import java.util.concurrent.Semaphore;\r
15 import java.util.concurrent.TimeUnit;\r
16 \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
21 \r
22 /**\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
27  * \r
28  * <p>\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
34  * \r
35  * <p>\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
43  * \r
44  * @author Tuukka Lehtonen\r
45  * \r
46  * @param <T> database request result type\r
47  * \r
48  * @see EvaluatingListener.Evaluation\r
49  * @see EvaluatingListener.Criterion\r
50  */\r
51 public class EvaluatingListener<T> implements Listener<T> {\r
52 \r
53     public static enum Evaluation {\r
54         /**\r
55          * Keep on listening to further results.\r
56          */\r
57         IGNORE,\r
58         /**\r
59          * Dispose listener and discard the results.\r
60          */\r
61         DISCARD,\r
62         /**\r
63          * Dispose listener and return the latest result.\r
64          */\r
65         ACCEPT\r
66     }\r
67 \r
68     /**\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
72      * \r
73      * @param <T> the type of the result\r
74      */\r
75     public static interface Criterion<T> {\r
76         Evaluation evaluate(T result);\r
77     }\r
78 \r
79     /**\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
83      */\r
84     private volatile Criterion<T> criterion;\r
85     private T                     result;\r
86     private Throwable             exception;\r
87     private Semaphore             wait = new Semaphore(0);\r
88 \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
93     }\r
94 \r
95     /**\r
96      * @param session\r
97      * @param request\r
98      * @param criterion\r
99      * @param timeout\r
100      * @param unit\r
101      * @return\r
102      * @throws InterruptedException \r
103      * @throws DatabaseException \r
104      */\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
110         l.dispose();\r
111         l.throwPossibleException();\r
112         return l.getResult();\r
113     }\r
114 \r
115     public T waitForResult() throws InterruptedException {\r
116         wait.acquire();\r
117         return getResult();\r
118     }\r
119 \r
120     public boolean tryWaitForResult(long timeout, TimeUnit unit) throws InterruptedException {\r
121         return wait.tryAcquire(timeout, unit);\r
122     }\r
123 \r
124     public T getResult() {\r
125         return result;\r
126     }\r
127 \r
128     public Throwable getException() {\r
129         return exception;\r
130     }\r
131 \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
137         }\r
138     }\r
139 \r
140     @Override\r
141     public void execute(T result) {\r
142         Criterion<T> crit = criterion;\r
143         if (crit == null)\r
144             return;\r
145         EvaluatingListener.Evaluation e = crit.evaluate(result);\r
146         switch (e) {\r
147             case IGNORE:\r
148                 ignored(result);\r
149                 return;\r
150             case ACCEPT:\r
151                 this.result = result;\r
152                 try {\r
153                     accepted(result);\r
154                 } finally {\r
155                     dispose();\r
156                     wait.release();\r
157                 }\r
158                 return;\r
159             case DISCARD:\r
160                 dispose();\r
161                 wait.release();\r
162                 return;\r
163         }\r
164     }\r
165 \r
166     /**\r
167      * Override to process results that were ignored.\r
168      * \r
169      * @param result\r
170      */\r
171     public void ignored(T result) {\r
172     }\r
173 \r
174     /**\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
177      * \r
178      * @param result\r
179      */\r
180     public void accepted(T result) {\r
181     }\r
182 \r
183     @Override\r
184     public void exception(Throwable t) {\r
185         this.exception = t;\r
186         dispose();\r
187         wait.release();\r
188     }\r
189 \r
190     private void dispose() {\r
191         this.criterion = null;\r
192     }\r
193 \r
194     @Override\r
195     public boolean isDisposed() {\r
196         return criterion == null;\r
197     }\r
198 \r
199 }