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}