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.behaviors;
011    
012    import static org.junit.Assert.assertEquals;
013    import static org.junit.Assert.assertFalse;
014    import static org.junit.Assert.assertNotNull;
015    import static org.junit.Assert.assertNotSame;
016    import static org.junit.Assert.assertSame;
017    import static org.junit.Assert.assertTrue;
018    import static org.junit.Assert.fail;
019    
020    import org.junit.Test;
021    import org.picocontainer.DefaultPicoContainer;
022    import org.picocontainer.containers.EmptyPicoContainer;
023    import org.picocontainer.lifecycle.NullLifecycleStrategy;
024    
025    public class StoringTestCase {
026    
027        public static class Foo {
028            public Foo(StringBuilder sb) {
029                sb.append("<Foo");
030            }
031        }
032    
033        public static class Bar {
034            private final Foo foo;
035            public Bar(StringBuilder sb, Foo foo) {
036                this.foo = foo;
037                sb.append("<Bar");
038            }
039        }
040    
041        @Test public void testThatForASingleThreadTheBehaviorIsTheSameAsPlainCaching() {
042    
043            DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
044            Storing storeCaching = new Storing();
045            DefaultPicoContainer child = new DefaultPicoContainer(storeCaching, new NullLifecycleStrategy(), parent);
046    
047            parent.addComponent(StringBuilder.class);
048            child.addComponent(Foo.class);
049    
050            StringBuilder sb = parent.getComponent(StringBuilder.class);
051            Foo foo = child.getComponent(Foo.class);
052            Foo foo2 = child.getComponent(Foo.class);
053            assertNotNull(foo);
054            assertNotNull(foo2);
055            assertEquals(foo,foo2);
056            assertEquals("<Foo", sb.toString());
057            assertEquals("Stored:ConstructorInjector-class org.picocontainer.behaviors.StoringTestCase$Foo", child.getComponentAdapter(Foo.class).toString());
058        }
059    
060        @Test public void testThatTwoThreadsHaveSeparatedCacheValues() {
061    
062            final Foo[] foos = new Foo[4];
063            final int[] sizes = new int[2];
064    
065            DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
066            final Storing storing = new Storing();
067            final DefaultPicoContainer child = new DefaultPicoContainer(storing, new NullLifecycleStrategy(), parent);
068    
069            parent.addComponent(StringBuilder.class);
070            child.addComponent(Foo.class);
071    
072            StringBuilder sb = parent.getComponent(StringBuilder.class);
073            assertEquals("store was not empty at outset for main thread", 0, storing.getCacheSize());
074            foos[0] = child.getComponent(Foo.class);
075    
076            Thread thread = new Thread("other") {
077                public void run() {
078                    sizes[0] = storing.getCacheSize();
079                    foos[1] = child.getComponent(Foo.class);
080                    foos[3] = child.getComponent(Foo.class);
081                    sizes[1] = storing.getCacheSize();
082                }
083            };
084            thread.start();
085            foos[2] = child.getComponent(Foo.class);
086            assertEquals("store was not sized 1 at end for main thread", 1, storing.getCacheSize());
087    
088            sleepALittle();
089    
090            assertNotNull(foos[0]);
091            assertNotNull(foos[1]);
092            assertNotNull(foos[2]);
093            assertNotNull(foos[3]);
094            assertSame(foos[0],foos[2]);
095            assertEquals(foos[1],foos[3]);
096            assertFalse(foos[0] == foos[1]);
097            assertEquals("<Foo<Foo", sb.toString());
098            assertEquals("Stored:ConstructorInjector-class org.picocontainer.behaviors.StoringTestCase$Foo", child.getComponentAdapter(Foo.class).toString());
099    
100            assertEquals("store was not empty at outset for other thread", 0, sizes[0]);
101            assertEquals("store was not sized 1 at end for other thread", 1, sizes[1]);
102        }
103    
104        @Test public void testThatTwoThreadsHaveSeparatedCacheValuesForThreeScopeScenario() {
105    
106            final Foo[] foos = new Foo[4];
107            final Bar[] bars = new Bar[4];
108    
109            DefaultPicoContainer appScope = new DefaultPicoContainer(new Caching());
110            final DefaultPicoContainer sessionScope = new DefaultPicoContainer(new Storing(), new NullLifecycleStrategy(), appScope);
111            final DefaultPicoContainer requestScope = new DefaultPicoContainer(new Storing(), new NullLifecycleStrategy(), sessionScope);
112    
113            appScope.addComponent(StringBuilder.class);
114            sessionScope.addComponent(Foo.class);
115            requestScope.addComponent(Bar.class);
116    
117            StringBuilder sb = appScope.getComponent(StringBuilder.class);
118            foos[0] = sessionScope.getComponent(Foo.class);
119            bars[0] = requestScope.getComponent(Bar.class);
120    
121            Thread thread = new Thread() {
122                public void run() {
123                    foos[1] = sessionScope.getComponent(Foo.class);
124                    bars[1] = requestScope.getComponent(Bar.class);
125                    foos[3] = sessionScope.getComponent(Foo.class);
126                    bars[3] = requestScope.getComponent(Bar.class);
127                }
128            };
129            thread.start();
130            foos[2] = sessionScope.getComponent(Foo.class);
131            bars[2] = requestScope.getComponent(Bar.class);
132            sleepALittle();
133    
134            assertSame(bars[0],bars[2]);
135            assertEquals(bars[1],bars[3]);
136            assertFalse(bars[0] == bars[1]);
137            assertSame(bars[0].foo,foos[0]);
138            assertSame(bars[1].foo,foos[1]);
139            assertSame(bars[2].foo,foos[2]);
140            assertSame(bars[3].foo,foos[3]);
141            assertEquals("<Foo<Bar<Foo<Bar", sb.toString());
142            assertEquals("Stored:ConstructorInjector-class org.picocontainer.behaviors.StoringTestCase$Foo", sessionScope.getComponentAdapter(Foo.class).toString());
143        }
144    
145        @Test public void testThatCacheMapCanBeReUsedOnASubsequentThreadSimulatingASessionConcept() {
146    
147            final Foo[] foos = new Foo[4];
148    
149            DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
150            final Storing storeCaching = new Storing();
151            final DefaultPicoContainer child = new DefaultPicoContainer(storeCaching, new NullLifecycleStrategy(), parent);
152    
153            parent.addComponent(StringBuilder.class);
154            child.addComponent(Foo.class);
155    
156            StringBuilder sb = parent.getComponent(StringBuilder.class);
157    
158            final Storing.StoreWrapper[] tmpMap = new Storing.StoreWrapper[1];
159            Thread thread = new Thread() {
160                public void run() {
161                    foos[0] = child.getComponent(Foo.class);
162                    foos[1] = child.getComponent(Foo.class);
163                    tmpMap[0] = storeCaching.getCacheForThread();
164    
165                }
166            };
167            thread.start();
168            sleepALittle();
169            thread = new Thread() {
170                public void run() {
171                    storeCaching.putCacheForThread(tmpMap[0]);
172                    foos[2] = child.getComponent(Foo.class);
173                    foos[3] = child.getComponent(Foo.class);
174                    tmpMap[0] = storeCaching.getCacheForThread();
175    
176                }
177            };
178            thread.start();
179            sleepALittle();
180    
181            assertNotNull(foos[0]);
182            assertNotNull(foos[1]);
183            assertNotNull(foos[2]);
184            assertNotNull(foos[3]);
185            assertSame(foos[0],foos[1]);
186            assertSame(foos[1],foos[2]);
187            assertSame(foos[2],foos[3]);
188            assertEquals("<Foo", sb.toString());
189            assertEquals("Stored:ConstructorInjector-class org.picocontainer.behaviors.StoringTestCase$Foo", child.getComponentAdapter(Foo.class).toString());
190        }
191    
192        @Test public void testThatCacheMapCanBeResetOnASubsequentThreadSimulatingASessionConcept() {
193    
194    
195            DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
196            final Storing storeCaching = new Storing();
197            final DefaultPicoContainer child = new DefaultPicoContainer(storeCaching, new NullLifecycleStrategy(), parent);
198    
199            parent.addComponent(StringBuilder.class);
200            child.addComponent(Foo.class);
201    
202            StringBuilder sb = parent.getComponent(StringBuilder.class);
203    
204            Foo one = child.getComponent(Foo.class);
205            Foo two = child.getComponent(Foo.class);
206    
207            assertNotNull(one);
208            assertNotNull(two);
209            assertSame(one,two);
210    
211            assertTrue(storeCaching.resetCacheForThread() instanceof Storing.StoreWrapper);
212    
213            Foo three = child.getComponent(Foo.class);
214            Foo four = child.getComponent(Foo.class);
215    
216            assertNotNull(three);
217            assertNotNull(four);
218            assertNotSame(one,three);
219            assertSame(three,four);
220    
221            assertEquals("<Foo<Foo", sb.toString());
222            assertEquals("Stored:ConstructorInjector-class org.picocontainer.behaviors.StoringTestCase$Foo", child.getComponentAdapter(Foo.class).toString());
223        }
224    
225        @Test public void testThatCacheMapCanBeDisabledSimulatingAnEndedRequest() {
226    
227            DefaultPicoContainer parent = new DefaultPicoContainer(new Caching());
228            final Storing storeCaching = new Storing();
229            final DefaultPicoContainer child = new DefaultPicoContainer(storeCaching, parent);
230    
231            parent.addComponent(StringBuilder.class);
232            child.addComponent(Foo.class);
233    
234            StringBuilder sb = parent.getComponent(StringBuilder.class);
235    
236            Foo one = child.getComponent(Foo.class);
237            Foo two = child.getComponent(Foo.class);
238    
239            assertNotNull(one);
240            assertNotNull(two);
241            assertSame(one,two);
242    
243            storeCaching.invalidateCacheForThread();
244    
245            try {
246                Foo three = child.getComponent(Foo.class);
247                fail("should have barfed");
248            } catch (UnsupportedOperationException e) {
249                // expected
250            }
251        }
252    
253    
254        private void sleepALittle() {
255            try {
256                Thread.sleep(100);
257            } catch (InterruptedException e) {
258            }
259        }
260    
261    
262    }