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