1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.jexl3;
18
19 import java.util.IdentityHashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22 import java.util.NoSuchElementException;
23 import java.util.concurrent.atomic.AtomicInteger;
24 //import java.lang.reflect.Field;
25 //import sun.misc.Unsafe;
26
27 /**
28 *
29 * An example arithmetic that uses object intrinsic monitors to synchronize get/set/iteration on Maps.
30 */
31 public class SynchronizedArithmetic extends JexlArithmetic {
32
33 /**
34 * An indirect way to determine we are actually calling what is needed.
35 * <p>
36 * This class counts how many times we called enter & exit; they should be balanced
37 */
38 public abstract static class AbstractMonitor {
39 /* Counts the number of times enter is called. */
40 private final AtomicInteger enters = new AtomicInteger();
41 /* Counts the number of times exit is called. */
42 private final AtomicInteger exits = new AtomicInteger();
43
44 /**
45 * The number of enter calls.
46 * @return how many enter were executed
47 */
48 public int getCount() {
49 return enters.get();
50 }
51
52 /**
53 * Tests whether the number of monitor enter is equals to the number of exits.
54 * @return true if balanced, false otherwise
55 */
56 public boolean isBalanced() {
57 return enters.get() == exits.get();
58 }
59
60 /**
61 * Enter an object monitor.
62 * @param o the monitored object
63 */
64 protected void monitorEnter(final Object o) {
65 enters.incrementAndGet();
66 }
67
68 /**
69 * Exits an object monitor.
70 * @param o the monitored object
71 */
72 protected void monitorExit(final Object o) {
73 exits.incrementAndGet();
74 }
75
76 }
77
78 /**
79 * Crude monitor replacement...
80 */
81 static class SafeMonitor extends AbstractMonitor {
82 private final Map<Object, Object> monitored = new IdentityHashMap<>();
83
84 @Override
85 protected void monitorEnter(final Object o) {
86 Object guard;
87 try {
88 while (true) {
89 synchronized (monitored) {
90 guard = monitored.get(o);
91 if (guard == null) {
92 guard = new Object();
93 monitored.put(o, guard);
94 super.monitorEnter(o);
95 break;
96 }
97 }
98 synchronized (guard) {
99 guard.wait();
100 }
101 }
102 } catch (final InterruptedException xint) {
103 Thread.currentThread().interrupt();
104 }
105 }
106
107 @Override protected void monitorExit(final Object o) {
108 final Object guard;
109 synchronized(monitored) {
110 guard = monitored.remove(o);
111 }
112 if (guard != null) {
113 synchronized(guard) {
114 guard.notifyAll();
115 }
116 super.monitorExit(o);
117 }
118 }
119 }
120
121 /**
122 * An iterator that implements Closeable (at least implements a close method)
123 * and uses monitors to protect iteration.
124 */
125 public class SynchronizedIterator implements /*Closeable,*/ Iterator<Object> {
126 private final Object monitored;
127 private Iterator<Object> iterator;
128
129 SynchronizedIterator(final Object locked, final Iterator<Object> ii) {
130 monitored = locked;
131 abstractMonitor.monitorEnter(monitored);
132 try {
133 iterator = ii;
134 } finally {
135 if (iterator == null) {
136 abstractMonitor.monitorExit(monitored);
137 }
138 }
139 }
140
141 //@Override
142 public void close() {
143 if (iterator != null) {
144 abstractMonitor.monitorExit(monitored);
145 iterator = null;
146 }
147 }
148
149 @Override
150 protected void finalize() throws Throwable {
151 close();
152 super.finalize();
153 }
154
155 @Override
156 public boolean hasNext() {
157 if (iterator == null) {
158 return false;
159 }
160 final boolean n = iterator.hasNext();
161 if (!n) {
162 close();
163 }
164 return n;
165 }
166
167 @Override
168 public Object next() {
169 if (iterator == null) {
170 throw new NoSuchElementException();
171 }
172 return iterator.next();
173 }
174
175 @Override
176 public void remove() {
177 if (iterator != null) {
178 iterator.remove();
179 }
180 }
181 }
182
183 /*
184 * You should know better than to use this...
185 */
186 // private static Unsafe UNSAFE;
187 // static {
188 // try {
189 // Field f = Unsafe.class.getDeclaredField("theUnsafe");
190 // f.setAccessible(true);
191 // UNSAFE = (Unsafe) f.get(null);
192 // } catch (Exception e) {
193 // UNSAFE = null;
194 // }
195 // }
196 //
197 // /**
198 // * Using the unsafe to enter & exit object intrinsic monitors.
199 // */
200 // static class UnsafeMonitor extends Monitor {
201 // @Override protected void monitorEnter(Object o) {
202 // UNSAFE.monitorEnter(o);
203 // super.monitorEnter(o);
204 // }
205 //
206 // @Override protected void monitorExit(Object o) {
207 // UNSAFE.monitorExit(o);
208 // super.monitorExit(o);
209 // }
210 // }
211
212 /**
213 * Monitor/synchronized protected access to gets/set/iterator on maps.
214 */
215 private final AbstractMonitor abstractMonitor;
216
217 /**
218 * A base synchronized arithmetic.
219 * @param abstractMonitor the synchronization monitor
220 * @param strict whether the arithmetic is strict or not
221 */
222 protected SynchronizedArithmetic(final AbstractMonitor abstractMonitor, final boolean strict) {
223 super(strict);
224 this.abstractMonitor = abstractMonitor;
225 }
226
227 /**
228 * Jexl pseudo-overload for array-like access get operator (map[key]) for maps.
229 * <p>synchronized(map) { return map.get(key); }
230 * @param map the map
231 * @param key the key
232 * @return the value associated to the key in the map
233 */
234 public Object arrayGet(final Map<?, ?> map, final Object key) {
235 abstractMonitor.monitorEnter(map);
236 try {
237 return map.get(key);
238 } finally {
239 abstractMonitor.monitorExit(map);
240 }
241 }
242
243 /**
244 * Jexl pseudo-overload for array-like access set operator (map[key] = value) for maps.
245 * <p>synchronized(map) { map.put(key, value); }
246 * @param map the map
247 * @param key the key
248 * @param value the value
249 */
250 public void arraySet(final Map<Object, Object> map, final Object key, final Object value) {
251 abstractMonitor.monitorEnter(map);
252 try {
253 map.put(key, value);
254 } finally {
255 abstractMonitor.monitorExit(map);
256 }
257 }
258
259 /**
260 * Creates an iterator on the map values that executes under the map monitor.
261 * @param map the map
262 * @return the iterator
263 */
264 public Iterator<Object> forEach(final Map<Object, Object> map) {
265 return new SynchronizedIterator(map, map.values().iterator());
266 }
267
268 /**
269 * Jexl pseudo-overload for property access get operator (map.key) for maps.
270 * <p>synchronized(map) { return map.get(key); }
271 *
272 * @param map the map
273 * @param key the key
274 * @return the value associated to the key in the map
275 */
276 public Object propertyGet(final Map<?, ?> map, final Object key) {
277 abstractMonitor.monitorEnter(map);
278 try {
279 return map.get(key);
280 } finally {
281 abstractMonitor.monitorExit(map);
282 }
283 }
284
285 /**
286 * Jexl pseudo-overload for array-like access set operator (map.key = value) for maps.
287 * <p>synchronized(map) { map.put(key, value); }
288 * @param map the map
289 * @param key the key
290 * @param value the value
291 */
292 public void propertySet(final Map<Object, Object> map, final Object key, final Object value) {
293 abstractMonitor.monitorEnter(map);
294 try {
295 map.put(key, value);
296 } finally {
297 abstractMonitor.monitorExit(map);
298 }
299 }
300 }