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 static org.apache.commons.lang3.LangAssertions.assertNullPointerException;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  import static org.junit.jupiter.api.Assertions.fail;
26  
27  import java.io.IOException;
28  import java.sql.SQLException;
29  
30  import org.apache.commons.lang3.function.FailableConsumer;
31  import org.apache.commons.lang3.function.FailableSupplier;
32  import org.junit.jupiter.api.Test;
33  
34  /**
35   * An abstract base class for tests of exceptions thrown during initialize and close methods
36   * on concrete {@code ConcurrentInitializer} implementations.
37   *
38   * This class provides some basic tests for initializer implementations. Derived
39   * class have to create a {@link ConcurrentInitializer} object on which the
40   * tests are executed.
41   *
42   * @param <T> Domain type.
43   */
44  public abstract class AbstractConcurrentInitializerCloseAndExceptionsTest<T> extends AbstractConcurrentInitializerTest<T> {
45  
46      protected static final class CloseableObject {
47          boolean closed;
48  
49          public void close() {
50              closed = true;
51          }
52  
53          public boolean isClosed() {
54              return closed;
55          }
56      }
57  
58      protected enum ExceptionToThrow {
59          IOException,
60          SQLException,
61          NullPointerException
62      }
63  
64      // The use of enums rather than accepting an Exception as the input means we can have
65      // multiple exception types on the method signature.
66      protected static CloseableObject methodThatThrowsException(final ExceptionToThrow input) throws IOException, SQLException, ConcurrentException {
67          switch (input) {
68          case IOException:
69              throw new IOException();
70          case SQLException:
71              throw new SQLException();
72          case NullPointerException:
73              throw new NullPointerException();
74          default:
75              fail();
76              return new CloseableObject();
77          }
78      }
79  
80      protected abstract ConcurrentInitializer<CloseableObject> createInitializerThatThrowsException(
81              FailableSupplier<CloseableObject, ? extends Exception> supplier, FailableConsumer<CloseableObject, ? extends Exception> closer);
82  
83      /**
84       * This method tests that if AbstractConcurrentInitializer.close catches a
85       * ConcurrentException it will rethrow it wrapped in a ConcurrentException
86       */
87      @SuppressWarnings("rawtypes")
88      @Test
89      void testCloserThrowsCheckedException() throws ConcurrentException {
90          final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
91                  CloseableObject::new,
92                  CloseableObject -> methodThatThrowsException(ExceptionToThrow.IOException));
93          try {
94              initializer.get();
95              ((AbstractConcurrentInitializer) initializer).close();
96              fail();
97          } catch (final Exception e) {
98              assertInstanceOf(ConcurrentException.class, e);
99              assertInstanceOf(IOException.class, e.getCause());
100         }
101     }
102 
103     /**
104      * This method tests that if AbstractConcurrentInitializer.close catches a
105      * RuntimeException it will throw it without wrapping it in a ConcurrentException
106      */
107     @SuppressWarnings("rawtypes")
108     @Test
109     void testCloserThrowsRuntimeException() throws ConcurrentException {
110         final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
111                 CloseableObject::new,
112                 CloseableObject -> methodThatThrowsException(ExceptionToThrow.NullPointerException));
113 
114         initializer.get();
115         assertNullPointerException(() -> {
116             ((AbstractConcurrentInitializer) initializer).close();
117             });
118     }
119 
120     /**
121      * This method tests that if AbstractConcurrentInitializer.initialize catches a checked
122      * exception it will rethrow it wrapped in a ConcurrentException
123      */
124     @SuppressWarnings("unchecked") //for NOP
125     @Test
126     void testSupplierThrowsCheckedException() {
127         final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
128                 () -> methodThatThrowsException(ExceptionToThrow.IOException),
129                 FailableConsumer.NOP);
130         assertThrows(ConcurrentException.class, () -> initializer.get());
131     }
132 
133     /**
134      * This method tests that if a AbstractConcurrentInitializer.initialize method catches a
135      * ConcurrentException it will rethrow it without wrapping it.
136      */
137     @Test
138     void testSupplierThrowsConcurrentException() {
139         final ConcurrentException concurrentException = new ConcurrentException();
140 
141         @SuppressWarnings("unchecked")
142         final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
143                 () -> {
144                     if ("test".equals("test")) {
145                         throw concurrentException;
146                     }
147                     return new CloseableObject();
148                 },
149                 FailableConsumer.NOP);
150         try {
151             initializer.get();
152             fail();
153         } catch (final ConcurrentException e) {
154             assertEquals(concurrentException, e);
155         }
156     }
157 
158     /**
159      * This method tests that if AbstractConcurrentInitializer.initialize catches a runtime exception
160      * it will not be wrapped in a ConcurrentException
161      */
162     @SuppressWarnings("unchecked")
163     @Test
164     void testSupplierThrowsRuntimeException() {
165         final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
166                 () -> methodThatThrowsException(ExceptionToThrow.NullPointerException),
167                 FailableConsumer.NOP);
168         assertNullPointerException(() -> initializer.get());
169     }
170 
171     /**
172      * This method tests that if AbstractConcurrentInitializer.close actually closes the wrapped object
173      */
174     @SuppressWarnings("rawtypes")
175     @Test
176     void testWorkingCloser() throws Exception {
177         final ConcurrentInitializer<CloseableObject> initializer = createInitializerThatThrowsException(
178                 CloseableObject::new,
179                 CloseableObject::close);
180 
181         final CloseableObject closeableObject = initializer.get();
182         assertFalse(closeableObject.isClosed());
183         ((AbstractConcurrentInitializer) initializer).close();
184         assertTrue(closeableObject.isClosed());
185     }
186 }