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 * An indirect way to determine we are actually calling what is needed.
34 * <p>
35 * This class counts how many times we called enter & exit; they should be balanced
36 */
37 public abstract static class AbstractMonitor {
38 /* Counts the number of times enter is called. */
39 private final AtomicInteger enters = new AtomicInteger();
40 /* Counts the number of times exit is called. */
41 private final AtomicInteger exits = new AtomicInteger();
42
43 /**
44 * The number of enter calls.
45 * @return how many enter were executed
46 */
47 public int getCount() {
48 return enters.get();
49 }
50
51 /**
52 * Tests whether the number of monitor enter is equals to the number of exits.
53 * @return true if balanced, false otherwise
54 */
55 public boolean isBalanced() {
56 return enters.get() == exits.get();
57 }
58
59 /**
60 * Enter an object monitor.
61 * @param o the monitored object
62 */
63 protected void monitorEnter(final Object o) {
64 enters.incrementAndGet();
65 }
66
67 /**
68 * Exits an object monitor.
69 * @param o the monitored object
70 */
71 protected void monitorExit(final Object o) {
72 exits.incrementAndGet();
73 }
74
75 }
76
77 /**
78 * Crude monitor replacement...
79 */
80 static class SafeMonitor extends AbstractMonitor {
81 private final Map<Object, Object> monitored = new IdentityHashMap<>();
82
83 @Override
84 protected void monitorEnter(final Object o) {
85 Object guard;
86 try {
87 while (true) {
88 synchronized (monitored) {
89 guard = monitored.get(o);
90 if (guard == null) {
91 guard = new Object();
92 monitored.put(o, guard);
93 super.monitorEnter(o);
94 break;
95 }
96 }
97 synchronized (guard) {
98 guard.wait();
99 }
100 }
101 } catch (final InterruptedException xint) {
102 // oops
103 }
104 }
105
106 @Override protected void monitorExit(final Object o) {
107 final Object guard;
108 synchronized(monitored) {
109 guard = monitored.remove(o);
110 }
111 if (guard != null) {
112 synchronized(guard) {
113 guard.notifyAll();
114 }
115 super.monitorExit(o);
116 }
117 }
118 }
119
120 /**
121 * An iterator that implements Closeable (at least implements a close method)
122 * and uses monitors to protect iteration.
123 */
124 public class SynchronizedIterator implements /*Closeable,*/ Iterator<Object> {
125 private final Object monitored;
126 private Iterator<Object> iterator;
127
128 SynchronizedIterator(final Object locked, final Iterator<Object> ii) {
129 monitored = locked;
130 abstractMonitor.monitorEnter(monitored);
131 try {
132 iterator = ii;
133 } finally {
134 if (iterator == null) {
135 abstractMonitor.monitorExit(monitored);
136 }
137 }
138 }
139
140 //@Override
141 public void close() {
142 if (iterator != null) {
143 abstractMonitor.monitorExit(monitored);
144 iterator = null;
145 }
146 }
147
148 @Override
149 protected void finalize() throws Throwable {
150 close();
151 super.finalize();
152 }
153
154 @Override
155 public boolean hasNext() {
156 if (iterator == null) {
157 return false;
158 }
159 final boolean n = iterator.hasNext();
160 if (!n) {
161 close();
162 }
163 return n;
164 }
165
166 @Override
167 public Object next() {
168 if (iterator == null) {
169 throw new NoSuchElementException();
170 }
171 return iterator.next();
172 }
173
174 @Override
175 public void remove() {
176 if (iterator != null) {
177 iterator.remove();
178 }
179 }
180 }
181
182 /*
183 * You should know better than to use this...
184 */
185 // private static Unsafe UNSAFE;
186 // static {
187 // try {
188 // Field f = Unsafe.class.getDeclaredField("theUnsafe");
189 // f.setAccessible(true);
190 // UNSAFE = (Unsafe) f.get(null);
191 // } catch (Exception e) {
192 // UNSAFE = null;
193 // }
194 // }
195 //
196 // /**
197 // * Using the unsafe to enter & exit object intrinsic monitors.
198 // */
199 // static class UnsafeMonitor extends Monitor {
200 // @Override protected void monitorEnter(Object o) {
201 // UNSAFE.monitorEnter(o);
202 // super.monitorEnter(o);
203 // }
204 //
205 // @Override protected void monitorExit(Object o) {
206 // UNSAFE.monitorExit(o);
207 // super.monitorExit(o);
208 // }
209 // }
210
211 /**
212 * Monitor/synchronized protected access to gets/set/iterator on maps.
213 */
214 private final AbstractMonitor abstractMonitor;
215
216 /**
217 * A base synchronized arithmetic.
218 * @param abstractMonitor the synchronization monitor
219 * @param strict whether the arithmetic is strict or not
220 */
221 protected SynchronizedArithmetic(final AbstractMonitor abstractMonitor, final boolean strict) {
222 super(strict);
223 this.abstractMonitor = abstractMonitor;
224 }
225
226 /**
227 * Jexl pseudo-overload for array-like access get operator (map[key]) for maps.
228 * <p>synchronized(map) { return map.get(key); }
229 * @param map the map
230 * @param key the key
231 * @return the value associated to the key in the map
232 */
233 public Object arrayGet(final Map<?, ?> map, final Object key) {
234 abstractMonitor.monitorEnter(map);
235 try {
236 return map.get(key);
237 } finally {
238 abstractMonitor.monitorExit(map);
239 }
240 }
241 /**
242 * Jexl pseudo-overload for array-like access set operator (map[key] = value) for maps.
243 * <p>synchronized(map) { map.put(key, value); }
244 * @param map the map
245 * @param key the key
246 * @param value the value
247 */
248 public void arraySet(final Map<Object, Object> map, final Object key, final Object value) {
249 abstractMonitor.monitorEnter(map);
250 try {
251 map.put(key, value);
252 } finally {
253 abstractMonitor.monitorExit(map);
254 }
255 }
256 /**
257 * Creates an iterator on the map values that executes under the map monitor.
258 * @param map the map
259 * @return the iterator
260 */
261 public Iterator<Object> forEach(final Map<Object, Object> map) {
262 return new SynchronizedIterator(map, map.values().iterator());
263 }
264
265 /**
266 * Jexl pseudo-overload for property access get operator (map.key) for maps.
267 * <p>synchronized(map) { return map.get(key); }
268 *
269 * @param map the map
270 * @param key the key
271 * @return the value associated to the key in the map
272 */
273 public Object propertyGet(final Map<?, ?> map, final Object key) {
274 abstractMonitor.monitorEnter(map);
275 try {
276 return map.get(key);
277 } finally {
278 abstractMonitor.monitorExit(map);
279 }
280 }
281
282 /**
283 * Jexl pseudo-overload for array-like access set operator (map.key = value) for maps.
284 * <p>synchronized(map) { map.put(key, value); }
285 * @param map the map
286 * @param key the key
287 * @param value the value
288 */
289 public void propertySet(final Map<Object, Object> map, final Object key, final Object value) {
290 abstractMonitor.monitorEnter(map);
291 try {
292 map.put(key, value);
293 } finally {
294 abstractMonitor.monitorExit(map);
295 }
296 }
297 }