package com.example.demo; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Bean; import org.springframework.context.event.EventListener; import org.springframework.http.HttpRequest; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.util.CollectionUtils; import org.springframework.web.client.RestTemplate; import java.io.IOException; import java.util.ArrayList; import java.util.List; @SpringBootApplication public class DemoApplication { class TestMiddleware implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { System.out.println("Executing " + request.getURI()); ClientHttpResponse response = execution.execute(request, body); return response; } } @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); return restTemplate; } @Bean public RestTemplate restTemplateFix() { List interceptors = new ArrayList<>(); restTemplate.setInterceptors(interceptors); RestTemplate restTemplate = new RestTemplate(); return restTemplate; } @Qualifier("restTemplate") RestTemplate restTemplate = new RestTemplate(); @Qualifier("restTemplateFix") RestTemplate restTemplateFix = new RestTemplate(); public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @EventListener(ApplicationReadyEvent.class) public void doSomethingAfterStartup() { //search for ConcurrentModificationException //what is ConcurrentModificationException //testConcurrentModificationExceptionInReadingIteration(); //trigger ConcurrentModificationException testConcurrentModificationExceptionInRestTemplate(); //fix one //testConcurrentModificationExceptionInRestTemplateFixWithSharedCollection(); //fix two //testConcurrentModificationExceptionInRestTemplateFixWithInterceptorsCopy(); //fix three //testConcurrentModificationExceptionInRestTemplateFixWithCheckInterceptorsCollection(); //fix four //testConcurrentModificationExceptionInRestTemplateFixWithSetInterceptorsOnce(); } private void testConcurrentModificationExceptionInRestTemplate() { for (int i = 0; i < 2000; i++) { new Thread(() -> { List interceptors = new ArrayList<>(); interceptors.add(new TestMiddleware()); restTemplate.setInterceptors(interceptors); ResponseEntity response = restTemplate.getForEntity("http://ip.jsontest.com/", String.class); System.out.println(response); }).start(); } } final List sharedCollection = new ArrayList<>(List.of(new ClientHttpRequestInterceptor[]{new TestMiddleware()})); private void testConcurrentModificationExceptionInRestTemplateFixWithSharedCollection() { for (int i = 0; i < 2000; i++) { new Thread(() -> { List interceptorsReference = restTemplate.getInterceptors(); interceptorsReference = sharedCollection; ResponseEntity response = restTemplate.getForEntity("http://ip.jsontest.com/", String.class); System.out.println(response); }).start(); } } private void testConcurrentModificationExceptionInRestTemplateFixWithInterceptorsCopy() { for (int i = 0; i < 2000; i++) { new Thread(() -> { List interceptorsCopy = new ArrayList<>(List.of(new ClientHttpRequestInterceptor[]{new TestMiddleware()})); List interceptorsReference = restTemplate.getInterceptors(); interceptorsReference = interceptorsCopy; ResponseEntity response = restTemplate.getForEntity("http://ip.jsontest.com/", String.class); System.out.println(response); }).start(); } } private void testConcurrentModificationExceptionInRestTemplateFixWithCheckInterceptorsCollection() { for (int i = 0; i < 2000; i++) { new Thread(() -> { List interceptors = restTemplate.getInterceptors(); if (CollectionUtils.isEmpty(interceptors)) { interceptors = new ArrayList<>(); interceptors.add(new TestMiddleware()); } restTemplate.setInterceptors(interceptors); ResponseEntity response = restTemplate.getForEntity("http://ip.jsontest.com/", String.class); System.out.println(response); }).start(); } } private void testConcurrentModificationExceptionInRestTemplateFixWithSetInterceptorsOnce() { for (int i = 0; i < 2000; i++) { new Thread(() -> { ResponseEntity response = restTemplateFix.getForEntity("http://ip.jsontest.com/", String.class); System.out.println(response); }).start(); } } private void testConcurrentModificationExceptionInReadingIteration() { List integers = new ArrayList(); System.out.println("before"); for (int i = 0; i < 20; i++) { int finalI = i; new Thread(() -> { System.out.println("thread" + finalI + " add item"); integers.add(finalI); }).start(); } new Thread(() -> { for (Integer integer : integers) { System.out.println("reading " + integers.get(integer)); } }).start(); System.out.println("after"); } }