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  
18  package org.apache.commons.lang3.builder;
19  
20  import java.util.LinkedList;
21  import java.util.Random;
22  
23  import org.apache.commons.lang3.AbstractLangTest;
24  import org.junit.jupiter.api.Disabled;
25  import org.junit.jupiter.api.Test;
26  
27  /**
28   * Tests concurrent access for {@link ReflectionToStringBuilder}.
29   * <p>
30   * The {@link ToStringStyle} class includes a registry to avoid infinite loops for objects with circular references. We
31   * want to make sure that we do not get concurrency exceptions accessing this registry.
32   * </p>
33   *
34   * @see <a href="https://issues.apache.org/jira/browse/LANG-762">[LANG-762] Handle or document ReflectionToStringBuilder
35   *      and ToStringBuilder for collections that are not thread safe</a>
36   */
37  class ReflectionToStringBuilderMutateInspectConcurrencyTest extends AbstractLangTest {
38  
39      final class InspectingClient implements Runnable {
40          private final TestFixture testFixture;
41  
42          InspectingClient(final TestFixture testFixture) {
43              this.testFixture = testFixture;
44          }
45  
46          @Override
47          public void run() {
48              ReflectionToStringBuilder.toString(testFixture);
49          }
50      }
51  
52      final class MutatingClient implements Runnable {
53          private final TestFixture testFixture;
54          private final Random random = new Random();
55  
56          MutatingClient(final TestFixture testFixture) {
57              this.testFixture = testFixture;
58          }
59  
60          @Override
61          public void run() {
62              if (random.nextBoolean()) {
63                  testFixture.add();
64              } else {
65                  testFixture.delete();
66              }
67          }
68      }
69  
70      final class TestFixture {
71          private final LinkedList<Integer> listField = new LinkedList<>();
72          private final Random random = new Random();
73          private final int N = 100;
74  
75          TestFixture() {
76              synchronized (this) {
77                  for (int i = 0; i < N; i++) {
78                      listField.add(Integer.valueOf(i));
79                  }
80              }
81          }
82  
83          public synchronized void add() {
84              listField.add(Integer.valueOf(random.nextInt(N)));
85          }
86  
87          public synchronized void delete() {
88              listField.remove(Integer.valueOf(random.nextInt(N)));
89          }
90      }
91  
92      @Test
93      @Disabled
94      void testConcurrency() {
95          final TestFixture testFixture = new TestFixture();
96          final int numMutators = 10;
97          final int numIterations = 10;
98          for (int i = 0; i < numIterations; i++) {
99              for (int j = 0; j < numMutators; j++) {
100                 final Thread t = new Thread(new MutatingClient(testFixture));
101                 t.start();
102                 final Thread s = new Thread(new InspectingClient(testFixture));
103                 s.start();
104             }
105         }
106     }
107 }