View Javadoc
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    *      http://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           * 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 }