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 *****************************************************************************/
010package org.picocontainer.behaviors;
011
012import static org.junit.Assert.assertEquals;
013import static org.junit.Assert.assertFalse;
014import static org.junit.Assert.assertNotNull;
015import static org.junit.Assert.assertSame;
016
017import org.junit.Test;
018import org.picocontainer.Characteristics;
019import org.picocontainer.DefaultPicoContainer;
020import org.picocontainer.classname.DefaultClassLoadingPicoContainer;
021import org.picocontainer.injectors.CompositeInjection;
022import org.picocontainer.injectors.ConstructorInjection;
023import org.picocontainer.injectors.SetterInjection;
024import org.picocontainer.lifecycle.NullLifecycleStrategy;
025import org.picocontainer.monitors.NullComponentMonitor;
026
027public class ThreadCachingTestCase {
028
029    public static class Foo {
030        public Foo(StringBuilder sb) {
031            sb.append("<Foo");
032        }
033    }
034
035    public static class Baz {
036
037        public Baz() {
038        }
039
040        public void setStringBuilder(StringBuilder sb) {
041                sb.append("<Baz");
042        }
043
044    }
045
046    public static class Qux {
047
048        private static final Object lock = new Object();
049
050        private static int CTR;
051
052        private int inst;
053
054        public Qux(StringBuilder sb) {
055            synchronized (lock) {
056                inst = CTR++;
057                sb.append("!").append(inst).append(" ");
058            }
059        }
060        public void setStringBuilder(StringBuilder sb) {
061            synchronized (lock) {
062                sb.append("<").append(inst).append(" ");
063            }
064        }
065
066        @Override
067        public String toString() {
068            return "baz2: " + inst;
069        }
070    }
071
072    public static class Bar {
073        private final Foo foo;
074        public Bar(StringBuilder sb, Foo foo) {
075            this.foo = foo;
076            sb.append("<Bar");
077        }
078    }
079
080    @Test public void testThatForASingleThreadTheBehaviorIsTheSameAsPlainCaching() {
081
082        DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
083        DefaultPicoContainer child = new DefaultPicoContainer(new ThreadCaching(), new NullLifecycleStrategy(), parent);
084
085        parent.addComponent(StringBuilder.class);
086        child.addComponent(Foo.class);
087
088        StringBuilder sb = parent.getComponent(StringBuilder.class);
089        Foo foo = child.getComponent(Foo.class);
090        Foo foo2 = child.getComponent(Foo.class);
091        assertNotNull(foo);
092        assertNotNull(foo2);
093        assertEquals(foo,foo2);
094        assertEquals("<Foo", sb.toString());
095        assertEquals("ThreadCached:ConstructorInjector-class org.picocontainer.behaviors.ThreadCachingTestCase$Foo", child.getComponentAdapter(Foo.class).toString());
096    }
097
098    @Test public void testThatForASingleThreadTheBehaviorIsTheSameAsPlainCachingWithSetterInjection() {
099
100        DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
101        DefaultPicoContainer child = new DefaultPicoContainer(new ThreadCaching().wrap(new SetterInjection()), new NullLifecycleStrategy(), parent);
102
103        parent.addComponent(StringBuilder.class);
104        child.addComponent(Baz.class);
105
106        StringBuilder sb = parent.getComponent(StringBuilder.class);
107        Baz baz = child.getComponent(Baz.class);
108        Baz baz2 = child.getComponent(Baz.class);
109        assertNotNull(baz);
110        assertNotNull(baz2);
111        assertEquals(baz,baz2);
112        assertEquals("<Baz", sb.toString());
113        assertEquals("ThreadCached:SetterInjector-class org.picocontainer.behaviors.ThreadCachingTestCase$Baz", child.getComponentAdapter(Baz.class).toString());
114    }
115
116    @Test public void testThatTwoThreadsHaveSeparatedCacheValues() {
117
118        final Foo[] foos = new Foo[4];
119
120        DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
121        final DefaultPicoContainer child = new DefaultPicoContainer(new ThreadCaching(), new NullLifecycleStrategy(), parent);
122
123        parent.addComponent(StringBuilder.class);
124        child.addComponent(Foo.class);
125
126        StringBuilder sb = parent.getComponent(StringBuilder.class);
127        foos[0] = child.getComponent(Foo.class);
128
129        Thread thread = new Thread() {
130            public void run() {
131                foos[1] = child.getComponent(Foo.class);
132                foos[3] = child.getComponent(Foo.class);
133            }
134        };
135        thread.start();
136        foos[2] = child.getComponent(Foo.class);
137        try {
138            Thread.sleep(100);
139        } catch (InterruptedException e) {
140        }
141
142        assertNotNull(foos[0]);
143        assertNotNull(foos[1]);
144        assertNotNull(foos[2]);
145        assertNotNull(foos[3]);
146        assertSame(foos[0],foos[2]);
147        assertEquals(foos[1],foos[3]);
148        assertFalse(foos[0] == foos[1]);
149        assertEquals("<Foo<Foo", sb.toString());
150        assertEquals("ThreadCached:ConstructorInjector-class org.picocontainer.behaviors.ThreadCachingTestCase$Foo", child.getComponentAdapter(Foo.class).toString());
151    }
152
153    @Test public void testThatTwoThreadsHaveSeparatedCacheValuesWithCompositeInjection() {
154
155        final Qux[] quxs = new Qux[4];
156
157        DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
158        final DefaultPicoContainer child = new DefaultPicoContainer(new ThreadCaching().wrap(
159                new CompositeInjection(new ConstructorInjection(), new SetterInjection())),
160                new NullLifecycleStrategy(), parent);
161
162        parent.addComponent(StringBuilder.class);
163        child.addComponent(Qux.class);
164
165        StringBuilder sb = parent.getComponent(StringBuilder.class);
166        quxs[0] = child.getComponent(Qux.class);
167
168        Thread thread = new Thread() {
169            public void run() {
170                quxs[1] = child.getComponent(Qux.class);
171                quxs[3] = child.getComponent(Qux.class);
172            }
173        };
174        thread.start();
175        quxs[2] = child.getComponent(Qux.class);
176        try {
177            Thread.sleep(100);
178        } catch (InterruptedException e) {
179        }
180
181        assertNotNull(quxs[0]);
182        assertNotNull(quxs[1]);
183        assertNotNull(quxs[2]);
184        assertNotNull(quxs[3]);
185        assertSame(quxs[0],quxs[2]);
186        assertEquals(quxs[1],quxs[3]);
187        assertFalse(quxs[0] == quxs[1]);
188        assertEquals("!0 <0 !1 <1", sb.toString().trim());
189        assertEquals("ThreadCached:CompositeInjector(ConstructorInjector+SetterInjector)-class org.picocontainer.behaviors.ThreadCachingTestCase$Qux", child.getComponentAdapter(Qux.class).toString());
190    }
191
192    @Test public void testThatTwoThreadsHaveSeparatedCacheValuesWithInstanceRegistrationAndClassLoadingPicoContainer() {
193
194        final Foo[] foos = new Foo[4];
195
196        DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
197        parent.change(Characteristics.USE_NAMES);
198        parent.addComponent(StringBuilder.class);
199
200        final DefaultClassLoadingPicoContainer child = new DefaultClassLoadingPicoContainer(new ThreadCaching(), new NullLifecycleStrategy(), parent, this.getClass().getClassLoader(), new NullComponentMonitor());
201        child.change(Characteristics.USE_NAMES);
202        child.addComponent(Foo.class);
203        child.addComponent("hello");
204
205        StringBuilder sb = parent.getComponent(StringBuilder.class);
206        foos[0] = child.getComponent(Foo.class);
207
208        Thread thread = new Thread() {
209            public void run() {
210                foos[1] = child.getComponent(Foo.class);
211                foos[3] = child.getComponent(Foo.class);
212            }
213        };
214        thread.start();
215        foos[2] = child.getComponent(Foo.class);
216        try {
217            Thread.sleep(100);
218        } catch (InterruptedException e) {
219        }
220
221        assertNotNull(foos[0]);
222        assertNotNull(foos[1]);
223        assertNotNull(foos[2]);
224        assertNotNull(foos[3]);
225        assertSame(foos[0],foos[2]);
226        assertEquals(foos[1],foos[3]);
227        assertFalse(foos[0] == foos[1]);
228        assertEquals("<Foo<Foo", sb.toString());
229        assertEquals("ThreadCached:ConstructorInjector-class org.picocontainer.behaviors.ThreadCachingTestCase$Foo", child.getComponentAdapter(Foo.class).toString());
230    }
231
232
233    @Test public void testThatTwoThreadsHaveSeparatedCacheValuesForThreeScopeScenario() {
234
235        final Foo[] foos = new Foo[4];
236        final Bar[] bars = new Bar[4];
237
238        DefaultPicoContainer appScope = new DefaultPicoContainer(new Caching());
239        final DefaultPicoContainer sessionScope = new DefaultPicoContainer(new ThreadCaching(), new NullLifecycleStrategy(), appScope);
240        final DefaultPicoContainer requestScope = new DefaultPicoContainer(new ThreadCaching(), new NullLifecycleStrategy(), sessionScope);
241
242        appScope.addComponent(StringBuilder.class);
243        sessionScope.addComponent(Foo.class);
244        requestScope.addComponent(Bar.class);
245
246        StringBuilder sb = appScope.getComponent(StringBuilder.class);
247        foos[0] = sessionScope.getComponent(Foo.class);
248        bars[0] = requestScope.getComponent(Bar.class);
249
250        Thread thread = new Thread() {
251            public void run() {
252                foos[1] = sessionScope.getComponent(Foo.class);
253                bars[1] = requestScope.getComponent(Bar.class);
254                foos[3] = sessionScope.getComponent(Foo.class);
255                bars[3] = requestScope.getComponent(Bar.class);
256            }
257        };
258        thread.start();
259        foos[2] = sessionScope.getComponent(Foo.class);
260        bars[2] = requestScope.getComponent(Bar.class);
261        try {
262            Thread.sleep(100);
263        } catch (InterruptedException e) {
264        }
265
266        assertSame(bars[0],bars[2]);
267        assertEquals(bars[1],bars[3]);
268        assertFalse(bars[0] == bars[1]);
269        assertSame(bars[0].foo,foos[0]);
270        assertSame(bars[1].foo,foos[1]);
271        assertSame(bars[2].foo,foos[2]);
272        assertSame(bars[3].foo,foos[3]);
273        assertEquals("<Foo<Bar<Foo<Bar", sb.toString());
274        assertEquals("ThreadCached:ConstructorInjector-class org.picocontainer.behaviors.ThreadCachingTestCase$Foo", sessionScope.getComponentAdapter(Foo.class).toString());
275    }
276
277
278}