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.lang3.concurrent;
18  
19  import java.util.concurrent.ConcurrentMap;
20  import java.util.concurrent.ExecutionException;
21  import java.util.concurrent.Future;
22  import java.util.concurrent.TimeUnit;
23  
24  import org.apache.commons.lang3.Validate;
25  import org.apache.commons.lang3.exception.ExceptionUtils;
26  
27  /**
28   * A utility class providing functionality related to the {@code
29   * java.util.concurrent} package.
30   *
31   * @since 3.0
32   */
33  public class ConcurrentUtils {
34  
35      /**
36       * A specialized {@link Future} implementation which wraps a constant value.
37       *
38       * @param <T> The type of the value wrapped by this class
39       */
40      static final class ConstantFuture<T> implements Future<T> {
41  
42          /** The constant value. */
43          private final T value;
44  
45          /**
46           * Creates a new instance of {@link ConstantFuture} and initializes it
47           * with the constant value.
48           *
49           * @param value The value (may be {@code null})
50           */
51          ConstantFuture(final T value) {
52              this.value = value;
53          }
54  
55          /**
56           * {@inheritDoc} The cancel operation is not supported. This
57           * implementation always returns <strong>false</strong>.
58           */
59          @Override
60          public boolean cancel(final boolean mayInterruptIfRunning) {
61              return false;
62          }
63  
64          /**
65           * {@inheritDoc} This implementation just returns the constant value.
66           */
67          @Override
68          public T get() {
69              return value;
70          }
71  
72          /**
73           * {@inheritDoc} This implementation just returns the constant value; it
74           * does not block, therefore the timeout has no meaning.
75           */
76          @Override
77          public T get(final long timeout, final TimeUnit unit) {
78              return value;
79          }
80  
81          /**
82           * {@inheritDoc} This implementation always returns <strong>false</strong>; there
83           * is no background process which could be canceled.
84           */
85          @Override
86          public boolean isCancelled() {
87              return false;
88          }
89  
90          /**
91           * {@inheritDoc} This implementation always returns <strong>true</strong> because
92           * the constant object managed by this {@link Future} implementation is
93           * always available.
94           */
95          @Override
96          public boolean isDone() {
97              return true;
98          }
99      }
100 
101     /**
102      * Tests whether the specified {@link Throwable} is a checked exception. If
103      * not, an exception is thrown.
104      *
105      * @param ex The {@link Throwable} to check
106      * @return A flag whether the passed in exception is a checked exception
107      * @throws IllegalArgumentException Thrown if the {@link Throwable} is not a checked exception.
108      */
109     static Throwable checkedException(final Throwable ex) {
110         Validate.isTrue(ExceptionUtils.isChecked(ex), "Not a checked exception: %s", ex);
111         return ex;
112     }
113 
114     /**
115      * Gets an implementation of {@link Future} that is immediately done
116      * and returns the specified constant value.
117      *
118      * <p>
119      * This can be useful to return a simple constant immediately from the
120      * concurrent processing, perhaps as part of avoiding nulls.
121      * A constant future can also be useful in testing.
122      * </p>
123      *
124      * @param <T> The type of the value used by this {@link Future} object
125      * @param value  The constant value to return, may be null
126      * @return An instance of Future that will return the value, never null
127      */
128     public static <T> Future<T> constantFuture(final T value) {
129         return new ConstantFuture<>(value);
130     }
131 
132     /**
133      * Checks if a concurrent map contains a key and creates a corresponding
134      * value if not. This method first checks the presence of the key in the
135      * given map. If it is already contained, its value is returned. Otherwise
136      * the {@code get()} method of the passed in {@link ConcurrentInitializer}
137      * is called. With the resulting object
138      * {@link #putIfAbsent(ConcurrentMap, Object, Object)} is called. This
139      * handles the case that in the meantime another thread has added the key to
140      * the map. Both the map and the initializer can be {@code null}; in this
141      * case this method simply returns {@code null}.
142      *
143      * @param <K> The type of the keys of the map
144      * @param <V> The type of the values of the map
145      * @param map The map to be modified
146      * @param key The key of the value to be added
147      * @param init The {@link ConcurrentInitializer} for creating the value
148      * @return The value stored in the map after this operation; this may or may
149      * not be the object created by the {@link ConcurrentInitializer}
150      * @throws ConcurrentException Thrown if the initializer throws an exception.
151      */
152     public static <K, V> V createIfAbsent(final ConcurrentMap<K, V> map, final K key,
153             final ConcurrentInitializer<V> init) throws ConcurrentException {
154         if (map == null || init == null) {
155             return null;
156         }
157 
158         final V value = map.get(key);
159         if (value == null) {
160             return putIfAbsent(map, key, init.get());
161         }
162         return value;
163     }
164 
165     /**
166      * Checks if a concurrent map contains a key and creates a corresponding
167      * value if not, suppressing checked exceptions. This method calls
168      * {@code createIfAbsent()}. If a {@link ConcurrentException} is thrown, it
169      * is caught and re-thrown as a {@link ConcurrentRuntimeException}.
170      *
171      * @param <K> The type of the keys of the map
172      * @param <V> The type of the values of the map
173      * @param map The map to be modified
174      * @param key The key of the value to be added
175      * @param init The {@link ConcurrentInitializer} for creating the value
176      * @return The value stored in the map after this operation; this may or may
177      * not be the object created by the {@link ConcurrentInitializer}
178      * @throws ConcurrentRuntimeException Thrown if the initializer throws an exception.
179      */
180     public static <K, V> V createIfAbsentUnchecked(final ConcurrentMap<K, V> map,
181             final K key, final ConcurrentInitializer<V> init) {
182         try {
183             return createIfAbsent(map, key, init);
184         } catch (final ConcurrentException cex) {
185             throw new ConcurrentRuntimeException(cex.getCause());
186         }
187     }
188 
189     /**
190      * Inspects the cause of the specified {@link ExecutionException} and
191      * creates a {@link ConcurrentException} with the checked cause if
192      * necessary. This method performs the following checks on the cause of the
193      * passed in exception:
194      * <ul>
195      * <li>If the passed in exception is {@code null} or the cause is
196      * {@code null}, this method returns {@code null}.</li>
197      * <li>If the cause is a runtime exception, it is directly thrown.</li>
198      * <li>If the cause is an error, it is directly thrown, too.</li>
199      * <li>In any other case the cause is a checked exception. The method then
200      * creates a {@link ConcurrentException}, initializes it with the cause, and
201      * returns it.</li>
202      * </ul>
203      *
204      * @param ex The exception to be processed
205      * @return A {@link ConcurrentException} with the checked cause
206      */
207     public static ConcurrentException extractCause(final ExecutionException ex) {
208         if (ex == null || ex.getCause() == null) {
209             return null;
210         }
211         ExceptionUtils.throwUnchecked(ex.getCause());
212         return new ConcurrentException(ex.getMessage(), ex.getCause());
213     }
214 
215     /**
216      * Inspects the cause of the specified {@link ExecutionException} and
217      * creates a {@link ConcurrentRuntimeException} with the checked cause if
218      * necessary. This method works exactly like
219      * {@link #extractCause(ExecutionException)}. The only difference is that
220      * the cause of the specified {@link ExecutionException} is extracted as a
221      * runtime exception. This is an alternative for client code that does not
222      * want to deal with checked exceptions.
223      *
224      * @param ex The exception to be processed
225      * @return A {@link ConcurrentRuntimeException} with the checked cause
226      */
227     public static ConcurrentRuntimeException extractCauseUnchecked(final ExecutionException ex) {
228         if (ex == null || ex.getCause() == null) {
229             return null;
230         }
231         ExceptionUtils.throwUnchecked(ex.getCause());
232         return new ConcurrentRuntimeException(ex.getMessage(), ex.getCause());
233     }
234 
235     /**
236      * Handles the specified {@link ExecutionException}. This method calls
237      * {@link #extractCause(ExecutionException)} for obtaining the cause of the
238      * exception - which might already cause an unchecked exception or an error
239      * being thrown. If the cause is a checked exception however, it is wrapped
240      * in a {@link ConcurrentException}, which is thrown. If the passed in
241      * exception is {@code null} or has no cause, the method simply returns
242      * without throwing an exception.
243      *
244      * @param ex The exception to be handled
245      * @throws ConcurrentException Thrown if the cause of the {@code ExecutionException} is a checked exception.
246      */
247     public static void handleCause(final ExecutionException ex) throws ConcurrentException {
248         final ConcurrentException cause = extractCause(ex);
249         if (cause != null) {
250             throw cause;
251         }
252     }
253 
254     /**
255      * Handles the specified {@link ExecutionException} and transforms it into a
256      * runtime exception. This method works exactly like
257      * {@link #handleCause(ExecutionException)}, but instead of a
258      * {@link ConcurrentException} it throws a
259      * {@link ConcurrentRuntimeException}. This is an alternative for client
260      * code that does not want to deal with checked exceptions.
261      *
262      * @param ex The exception to be handled
263      * @throws ConcurrentRuntimeException Thrown if the cause of the {@code ExecutionException} is a checked exception; this exception is then wrapped in the
264      *         thrown runtime exception.
265      */
266     public static void handleCauseUnchecked(final ExecutionException ex) {
267         final ConcurrentRuntimeException cause = extractCauseUnchecked(ex);
268         if (cause != null) {
269             throw cause;
270         }
271     }
272 
273     /**
274      * Invokes the specified {@link ConcurrentInitializer} and returns the
275      * object produced by the initializer. This method just invokes the {@code
276      * get()} method of the given {@link ConcurrentInitializer}. It is
277      * {@code null}-safe: if the argument is {@code null}, result is also
278      * {@code null}.
279      *
280      * @param <T> The type of the object produced by the initializer
281      * @param initializer The {@link ConcurrentInitializer} to be invoked
282      * @return The object managed by the {@link ConcurrentInitializer}
283      * @throws ConcurrentException Thrown if the {@link ConcurrentInitializer} throws an exception.
284      */
285     public static <T> T initialize(final ConcurrentInitializer<T> initializer)
286             throws ConcurrentException {
287         return initializer != null ? initializer.get() : null;
288     }
289 
290     /**
291      * Invokes the specified {@link ConcurrentInitializer} and transforms
292      * occurring exceptions to runtime exceptions. This method works like
293      * {@link #initialize(ConcurrentInitializer)}, but if the {@code
294      * ConcurrentInitializer} throws a {@link ConcurrentException}, it is
295      * caught, and the cause is wrapped in a {@link ConcurrentRuntimeException}.
296      * So client code does not have to deal with checked exceptions.
297      *
298      * @param <T> The type of the object produced by the initializer
299      * @param initializer The {@link ConcurrentInitializer} to be invoked
300      * @return The object managed by the {@link ConcurrentInitializer}
301      * @throws ConcurrentRuntimeException Thrown if the initializer throws an exception.
302      */
303     public static <T> T initializeUnchecked(final ConcurrentInitializer<T> initializer) {
304         try {
305             return initialize(initializer);
306         } catch (final ConcurrentException cex) {
307             throw new ConcurrentRuntimeException(cex.getCause());
308         }
309     }
310 
311     /**
312      * Puts a value in the specified {@link ConcurrentMap} if the key is not yet
313      * present. This method works similar to the {@code putIfAbsent()} method of
314      * the {@link ConcurrentMap} interface, but the value returned is different.
315      * Basically, this method is equivalent to the following code fragment:
316      *
317      * <pre>
318      * if (!map.containsKey(key)) {
319      *     map.put(key, value);
320      *     return value;
321      * } else {
322      *     return map.get(key);
323      * }
324      * </pre>
325      *
326      * <p>
327      * except that the action is performed atomically. So this method always
328      * returns the value which is stored in the map.
329      * </p>
330      * <p>
331      * This method is {@code null}-safe: It accepts a {@code null} map as input
332      * without throwing an exception. In this case the return value is
333      * {@code null}, too.
334      * </p>
335      *
336      * @param <K> The type of the keys of the map
337      * @param <V> The type of the values of the map
338      * @param map The map to be modified
339      * @param key The key of the value to be added
340      * @param value The value to be added
341      * @return The value stored in the map after this operation
342      */
343     public static <K, V> V putIfAbsent(final ConcurrentMap<K, V> map, final K key, final V value) {
344         if (map == null) {
345             return null;
346         }
347         final V result = map.putIfAbsent(key, value);
348         return result != null ? result : value;
349     }
350 
351     /**
352      * Private constructor so that no instances can be created. This class
353      * contains only static utility methods.
354      */
355     private ConcurrentUtils() {
356     }
357 
358 }