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  
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   * @since 3.1
37   */
38  public class ReflectionToStringBuilderMutateInspectConcurrencyTest extends AbstractLangTest {
39  
40      final class InspectingClient implements Runnable {
41          private final TestFixture testFixture;
42  
43          InspectingClient(final TestFixture testFixture) {
44              this.testFixture = testFixture;
45          }
46  
47          @Override
48          public void run() {
49              ReflectionToStringBuilder.toString(testFixture);
50          }
51      }
52  
53      final class MutatingClient implements Runnable {
54          private final TestFixture testFixture;
55          private final Random random = new Random();
56  
57          MutatingClient(final TestFixture testFixture) {
58              this.testFixture = testFixture;
59          }
60  
61          @Override
62          public void run() {
63              if (random.nextBoolean()) {
64                  testFixture.add();
65              } else {
66                  testFixture.delete();
67              }
68          }
69      }
70  
71      final class TestFixture {
72          private final LinkedList<Integer> listField = new LinkedList<>();
73          private final Random random = new Random();
74          private final int N = 100;
75  
76          TestFixture() {
77              synchronized (this) {
78                  for (int i = 0; i < N; i++) {
79                      listField.add(Integer.valueOf(i));
80                  }
81              }
82          }
83  
84          public synchronized void add() {
85              listField.add(Integer.valueOf(random.nextInt(N)));
86          }
87  
88          public synchronized void delete() {
89              listField.remove(Integer.valueOf(random.nextInt(N)));
90          }
91      }
92  
93      @Test
94      @Disabled
95      public void testConcurrency() {
96          final TestFixture testFixture = new TestFixture();
97          final int numMutators = 10;
98          final int numIterations = 10;
99          for (int i = 0; i < numIterations; i++) {
100             for (int j = 0; j < numMutators; j++) {
101                 final Thread t = new Thread(new MutatingClient(testFixture));
102                 t.start();
103                 final Thread s = new Thread(new InspectingClient(testFixture));
104                 s.start();
105             }
106         }
107     }
108 }