001 /*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved. *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file. *
007 * *
008 * Original code by *
009 *****************************************************************************/
010 package org.picocontainer.defaults;
011
012 import java.util.ArrayList;
013 import java.util.Collections;
014 import java.util.Date;
015 import java.util.List;
016
017 import junit.framework.TestCase;
018
019 import org.picocontainer.ComponentAdapter;
020 import org.picocontainer.PicoContainer;
021
022 /**
023 * @author Thomas Heller
024 * @author Aslak Hellesøy
025 * @author Jörg Schaible
026 * @version $Revision: 1572 $
027 */
028 public class SynchronizedComponentAdapterTestCase extends TestCase {
029 private Runner[] runner = new Runner[3];
030 private int blockerCounter = 0;
031
032 class Runner implements Runnable {
033 public RuntimeException exception;
034 public Blocker blocker;
035 private PicoContainer pico;
036
037 public Runner(PicoContainer pico) {
038 this.pico = pico;
039 }
040
041 public void run() {
042 try {
043 blocker = (Blocker) pico.getComponentInstance("key");
044 } catch (RuntimeException e) {
045 exception = e;
046 }
047 }
048 }
049
050 public class Blocker {
051 public Blocker() throws InterruptedException {
052 final Thread thread = Thread.currentThread();
053 synchronized (thread) {
054 SynchronizedComponentAdapterTestCase.this.blockerCounter++;
055 thread.wait();
056 }
057 }
058 }
059
060 private void initTest(ComponentAdapter componentAdapter) throws InterruptedException {
061 DefaultPicoContainer pico = new DefaultPicoContainer();
062 pico.registerComponentInstance(this);
063 pico.registerComponent(componentAdapter);
064 blockerCounter = 0;
065
066 for(int i = 0; i < runner.length; ++i) {
067 runner[i] = new Runner(pico);
068 }
069
070 Thread racer[] = new Thread[runner.length];
071 for(int i = 0; i < racer.length; ++i) {
072 racer[i] = new Thread(runner[i]);
073 }
074
075 for(int i = 0; i < racer.length; ++i) {
076 racer[i].start();
077 Thread.sleep(250);
078 }
079
080 for(int i = 0; i < racer.length; ++i) {
081 synchronized (racer[i]) {
082 racer[i].notify();
083 }
084 }
085
086 for(int i = 0; i < racer.length; ++i) {
087 racer[i].join();
088 }
089 }
090
091 public void testRaceConditionIsHandledBySynchronizedComponentAdapter() throws InterruptedException {
092 ComponentAdapter componentAdapter = new CachingComponentAdapter(new ConstructorInjectionComponentAdapter("key", Blocker.class));
093 SynchronizedComponentAdapter synchronizedComponentAdapter = new SynchronizedComponentAdapter(componentAdapter);
094 initTest(synchronizedComponentAdapter);
095
096 assertEquals(1, blockerCounter);
097 for(int i = 0; i < runner.length; ++i) {
098 assertNull(runner[i].exception);
099 }
100 for(int i = 0; i < runner.length; ++i) {
101 assertNotNull(runner[i].blocker);
102 }
103 for(int i = 1; i < runner.length; ++i) {
104 assertSame(runner[0].blocker, runner[i].blocker);
105 }
106 }
107
108 public void testRaceConditionIsNotHandledWithoutSynchronizedComponentAdapter() throws InterruptedException {
109 ComponentAdapter componentAdapter = new CachingComponentAdapter(new ConstructorInjectionComponentAdapter("key", Blocker.class));
110 initTest(componentAdapter);
111
112 assertNull(runner[0].exception);
113 assertEquals(3, blockerCounter);
114 for(int i = 1; i < runner.length; ++i) {
115 assertNull(runner[i].exception);
116 }
117 }
118
119 public void THIS_NATURALLY_FAILS_testSingletonCreationRace() throws InterruptedException {
120 DefaultPicoContainer pico = new DefaultPicoContainer();
121 pico.registerComponentImplementation("slow", SlowCtor.class);
122 runConcurrencyTest(pico);
123 }
124
125 public void THIS_NATURALLY_FAILS_testSingletonCreationWithSynchronizedAdapter() throws InterruptedException {
126 DefaultPicoContainer pico = new DefaultPicoContainer();
127 pico.registerComponent(new CachingComponentAdapter(new SynchronizedComponentAdapter(new ConstructorInjectionComponentAdapter("slow", SlowCtor.class))));
128 runConcurrencyTest(pico);
129 }
130
131 // This is overkill - an outer sync adapter is enough
132 public void testSingletonCreationWithSynchronizedAdapterAndDoubleLocking() throws InterruptedException {
133 DefaultPicoContainer pico = new DefaultPicoContainer();
134 pico.registerComponent(new SynchronizedComponentAdapter(new CachingComponentAdapter(new SynchronizedComponentAdapter(new ConstructorInjectionComponentAdapter("slow", SlowCtor.class)))));
135 runConcurrencyTest(pico);
136 }
137
138 public void testSingletonCreationWithSynchronizedAdapterOutside() throws InterruptedException {
139 DefaultPicoContainer pico = new DefaultPicoContainer();
140 pico.registerComponent(new SynchronizedComponentAdapter(new CachingComponentAdapter(new ConstructorInjectionComponentAdapter("slow", SlowCtor.class))));
141 runConcurrencyTest(pico);
142 }
143
144 public void testSingletonCreationWithSynchronizedAdapterOutsideUsingFactory() throws InterruptedException {
145 DefaultPicoContainer pico = new DefaultPicoContainer(
146 new SynchronizedComponentAdapterFactory(
147 new CachingComponentAdapterFactory(
148 new ConstructorInjectionComponentAdapterFactory()
149 )
150 )
151 );
152 pico.registerComponentImplementation("slow", SlowCtor.class);
153 runConcurrencyTest(pico);
154 }
155
156 private void runConcurrencyTest(final DefaultPicoContainer pico) throws InterruptedException {
157 int size = 10;
158
159 Thread[] threads = new Thread[size];
160
161 final List out = Collections.synchronizedList(new ArrayList());
162
163 for (int i = 0; i < size; i++) {
164
165 threads[i] = new Thread(new Runnable() {
166 public void run() {
167 try {
168 out.add(pico.getComponentInstance("slow"));
169 } catch (Exception e) {
170 // add ex? is e.equals(anotherEOfTheSameType) == true?
171 out.add(new Date()); // add something else to indicate miss
172 }
173 }
174 });
175 }
176
177 for (int i = 0; i < threads.length; i++) {
178 threads[i].start();
179 }
180 for (int i = 0; i < threads.length; i++) {
181 threads[i].join();
182 }
183
184 List differentInstances = new ArrayList();
185
186 for (int i = 0; i < out.size(); i++) {
187 Object o = out.get(i);
188
189 if (!differentInstances.contains(o))
190 differentInstances.add(o);
191 }
192
193 assertTrue("Only one singleton instance was created [we have " + differentInstances.size() + "]", differentInstances.size() == 1);
194 }
195
196 public static class SlowCtor {
197 public SlowCtor() throws InterruptedException {
198 Thread.sleep(50);
199 }
200 }
201 }