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    *      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 }