001    /**
002     * Copyright 2005-2014 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krad.data.provider;
017    
018    import org.apache.commons.lang.math.RandomUtils;
019    import org.junit.Before;
020    import org.junit.Test;
021    import org.kuali.rice.krad.data.provider.impl.ProviderRegistryImpl;
022    
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    import java.util.Collections;
026    import java.util.List;
027    import java.util.concurrent.BlockingQueue;
028    import java.util.concurrent.Callable;
029    import java.util.concurrent.ExecutorService;
030    import java.util.concurrent.Executors;
031    import java.util.concurrent.LinkedBlockingQueue;
032    import java.util.concurrent.TimeUnit;
033    
034    import static junit.framework.Assert.assertEquals;
035    import static org.junit.Assert.assertNull;
036    import static org.junit.Assert.assertSame;
037    import static org.mockito.Matchers.any;
038    import static org.mockito.Matchers.eq;
039    import static org.mockito.Mockito.mock;
040    import static org.mockito.Mockito.when;
041    
042    /**
043     * Tests ProviderRegistryImpl
044     */
045    public class ProviderRegistryImplTest {
046        // various Provider interfaces for testing
047        private static interface CustomProvider extends Provider {}
048        private static interface MultiProvider extends PersistenceProvider, MetadataProvider, CustomProvider {}
049    
050        // test types
051        private static class A {}
052        private static class B {}
053        private static Class<A> TYPE_A = A.class;
054        private static Class<B> TYPE_B = B.class;
055    
056        private ProviderRegistry registry;
057    
058        @Before
059        public void setup() {
060            registry = new ProviderRegistryImpl();
061        }
062    
063        /**
064         * Test empty state
065         */
066        @Test
067        public void testEmpty() {
068            assertEquals(0, registry.getProviders().size());
069            assertEquals(0, registry.getMetadataProviders().size());
070            assertEquals(0, registry.getProvidersForType(MetadataProvider.class).size());
071            assertEquals(0, registry.getProvidersForType(PersistenceProvider.class).size());
072            assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
073            assertNull(registry.getPersistenceProvider(String.class));
074        }
075    
076        @Test(expected=IllegalArgumentException.class)
077        public void testRegisterProviderIllegalArgument() {
078            registry.registerProvider(null);
079        }
080    
081        @Test(expected=IllegalArgumentException.class)
082        public void testUnregisterProviderIllegalArgument() {
083            registry.unregisterProvider(null);
084        }
085    
086        @Test(expected=IllegalArgumentException.class)
087        public void testGetProvidersForTypeIllegalArgument() {
088            registry.getProvidersForType(null);
089        }
090    
091        @Test(expected=IllegalArgumentException.class)
092        public void testGetPersistenceProviderIllegalArgument() {
093            registry.getPersistenceProvider(null);
094        }
095    
096        /**
097         * Registers various Provider implementations and tests getters
098         */
099        @Test
100        public void testRegisterProviders() {
101            registry.registerProvider(mock(Provider.class));
102    
103            assertEquals(1, registry.getProviders().size());
104            assertEquals(0, registry.getMetadataProviders().size());
105            assertEquals(0, registry.getProvidersForType(MetadataProvider.class).size());
106            assertEquals(0, registry.getProvidersForType(PersistenceProvider.class).size());
107            assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
108            assertNull(registry.getPersistenceProvider(String.class));
109    
110            // create mock metadataprovider that handles any dataobjecttype
111            MetadataProvider mockMetadataProvider = mock(MetadataProvider.class);
112            when(mockMetadataProvider.handles(any(Class.class))).thenReturn(true);
113    
114            registry.registerProvider(mockMetadataProvider);
115    
116            assertEquals(2, registry.getProviders().size());
117            assertEquals(1, registry.getMetadataProviders().size());
118            assertEquals(1, registry.getProvidersForType(MetadataProvider.class).size());
119            assertEquals(0, registry.getProvidersForType(PersistenceProvider.class).size());
120            assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
121            assertNull(registry.getPersistenceProvider(String.class));
122            assertSame(mockMetadataProvider, registry.getMetadataProvider(String.class));
123    
124            assertEquals(2, registry.getProviders().size());
125            assertEquals(1, registry.getMetadataProviders().size());
126            assertEquals(1, registry.getProvidersForType(MetadataProvider.class).size());
127            assertEquals(0, registry.getProvidersForType(PersistenceProvider.class).size());
128            assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
129            assertNull(registry.getPersistenceProvider(String.class));
130            assertSame(mockMetadataProvider, registry.getMetadataProvider(String.class));
131    
132            PersistenceProvider pp = mock(PersistenceProvider.class);
133            when(pp.handles(TYPE_B)).thenReturn(true);
134            registry.registerProvider(pp);
135    
136            assertEquals(3, registry.getProviders().size());
137            assertEquals(1, registry.getMetadataProviders().size());
138            assertEquals(1, registry.getProvidersForType(MetadataProvider.class).size());
139            assertEquals(1, registry.getProvidersForType(PersistenceProvider.class).size());
140            assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
141            assertNull(registry.getPersistenceProvider(String.class));
142            assertSame(pp, registry.getPersistenceProvider(B.class));
143            assertSame(mockMetadataProvider, registry.getMetadataProvider(String.class));
144    
145            registry.registerProvider(mock(CustomProvider.class));
146    
147            assertEquals(4, registry.getProviders().size());
148            assertEquals(1, registry.getMetadataProviders().size());
149            assertEquals(1, registry.getProvidersForType(MetadataProvider.class).size());
150            assertEquals(1, registry.getProvidersForType(PersistenceProvider.class).size());
151            assertEquals(1, registry.getProvidersForType(CustomProvider.class).size());
152            assertNull(registry.getPersistenceProvider(String.class));
153            assertSame(pp, registry.getPersistenceProvider(B.class));
154            assertSame(mockMetadataProvider, registry.getMetadataProvider(String.class));
155    
156            registry.registerProvider(mock(MultiProvider.class));
157    
158            assertEquals(5, registry.getProviders().size());
159            assertEquals(2, registry.getMetadataProviders().size());
160            assertEquals(2, registry.getProvidersForType(MetadataProvider.class).size());
161            assertEquals(2, registry.getProvidersForType(PersistenceProvider.class).size());
162            assertEquals(2, registry.getProvidersForType(CustomProvider.class).size());
163            assertNull(registry.getPersistenceProvider(String.class));
164            assertSame(pp, registry.getPersistenceProvider(B.class));
165            // returns the *first* metadataprovider that handles the given type
166            assertSame(mockMetadataProvider, registry.getMetadataProvider(String.class));
167        }
168    
169        /**
170         * Verifies duplicate providers can't be registered
171         */
172        @Test
173        public void testRegisterDuplicateProviders() {
174            Provider p = mock(Provider.class);
175            MetadataProvider mp1 = mock(MetadataProvider.class);
176            MetadataProvider mp2 = mock(MetadataProvider.class);
177    
178            registry.registerProvider(p);
179            assertEquals(1, registry.getProviders().size());
180            registry.registerProvider(p);
181            assertEquals(1, registry.getProviders().size());
182    
183            registry.registerProvider(mp1);
184            assertEquals(2, registry.getProviders().size());
185            assertEquals(1, registry.getMetadataProviders().size());
186            registry.registerProvider(mp1);
187            assertEquals(2, registry.getProviders().size());
188            assertEquals(1, registry.getMetadataProviders().size());
189    
190            registry.registerProvider(mp2);
191            assertEquals(3, registry.getProviders().size());
192            assertEquals(2, registry.getMetadataProviders().size());
193            registry.registerProvider(mp2);
194            assertEquals(3, registry.getProviders().size());
195            assertEquals(2, registry.getMetadataProviders().size());
196        }
197    
198        /**
199         * Tests unregistering after registering
200         */
201        @Test
202        public void testRegisterUnregister() {
203            Provider a = mock(Provider.class);
204            Provider b = mock(Provider.class);
205            Provider c = mock(Provider.class);
206            Provider d = mock(Provider.class);
207    
208            registry.registerProvider(a);
209            registry.registerProvider(b);
210            registry.registerProvider(c);
211            registry.registerProvider(d);
212    
213            assertEquals(4, registry.getProviders().size());
214    
215            registry.unregisterProvider(c);
216            registry.unregisterProvider(b);
217            registry.unregisterProvider(d);
218            registry.unregisterProvider(a);
219    
220            assertEquals(0, registry.getProviders().size());
221        }
222    
223        /**
224         * Verifies ProviderRegistryImpl is threadsafe
225         */
226        @Test
227        public void testConcurrency() throws InterruptedException {
228            final Class<? extends Provider>[] TYPES = new Class[] {
229                Provider.class, MetadataProvider.class,
230                PersistenceProvider.class, CustomProvider.class
231            };
232    
233            int providers = 50;
234            int threads = providers * 2; // just use live threads for all consumers/producers to ensure no consumer deadlock
235    
236            final BlockingQueue<Provider> queue = new LinkedBlockingQueue<Provider>();
237            ExecutorService threadpool = Executors.newFixedThreadPool(threads);
238    
239            Callable<Object>[] producers = new Callable[providers];
240            Callable<Object>[] consumers = new Callable[providers];
241            Callable<Object> producer = new Callable<Object>() {
242                @Override
243                public Object call() throws Exception {
244                    Provider p = mock(TYPES[RandomUtils.nextInt(5)]);
245                    registry.registerProvider(p);
246                    queue.add(p);
247                    return null;
248                }
249            };
250            Callable<Object> consumer = new Callable<Object>() {
251                @Override
252                public Object call() throws Exception {
253                    Provider p = queue.take();
254                    registry.unregisterProvider(p);
255                    return null;
256                }
257            };
258    
259            Arrays.fill(producers, producer);
260            Arrays.fill(consumers, consumer);
261    
262            List<Callable<Object>> tasks = new ArrayList<Callable<Object>>(providers * 2);
263            tasks.addAll(Arrays.asList(producers));
264            tasks.addAll(Arrays.asList(consumers));
265            Collections.shuffle(tasks);
266    
267            System.out.println("Registering and unregistering " + providers + " providers");
268            threadpool.invokeAll(tasks, 10, TimeUnit.SECONDS);
269    
270            // all producers and consumers should have run, we should be back at 0 providers registered
271            assertEquals(0, registry.getProviders().size());
272        }
273    
274        /**
275         * Tests registration and lookup of multiple PersistenceProviders
276         */
277        @Test
278        public void testRegisterPersistenceProviders() {
279            PersistenceProvider mockA = mock(PersistenceProvider.class);
280            when(mockA.handles(eq(TYPE_A))).thenReturn(true);
281    
282            PersistenceProvider mockB = mock(PersistenceProvider.class);
283            when(mockB.handles(eq(TYPE_B))).thenReturn(true);
284    
285            PersistenceProvider mockAB = mock(PersistenceProvider.class);
286            when(mockAB.handles(eq(TYPE_A))).thenReturn(true);
287            when(mockAB.handles(eq(TYPE_B))).thenReturn(true);
288    
289            registry.registerProvider(mockA);
290    
291            assertEquals(1, registry.getProviders().size());
292            assertEquals(0, registry.getMetadataProviders().size());
293            assertEquals(1, registry.getProvidersForType(PersistenceProvider.class).size());
294            assertEquals(0, registry.getProvidersForType(MetadataProvider.class).size());
295            assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
296            assertSame(mockA, registry.getPersistenceProvider(A.class));
297    
298            registry.registerProvider(mockB);
299    
300            assertEquals(2, registry.getProviders().size());
301            assertEquals(0, registry.getMetadataProviders().size());
302            assertEquals(2, registry.getProvidersForType(PersistenceProvider.class).size());
303            assertEquals(0, registry.getProvidersForType(MetadataProvider.class).size());
304            assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
305            assertSame(mockA, registry.getPersistenceProvider(A.class));
306            assertSame(mockB, registry.getPersistenceProvider(B.class));
307    
308            registry.registerProvider(mockAB);
309    
310            assertEquals(3, registry.getProviders().size());
311            assertEquals(0, registry.getMetadataProviders().size());
312            assertEquals(3, registry.getProvidersForType(PersistenceProvider.class).size());
313            assertEquals(0, registry.getProvidersForType(MetadataProvider.class).size());
314            assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
315            assertSame(mockA, registry.getPersistenceProvider(A.class)); // still returns mockA
316            assertSame(mockB, registry.getPersistenceProvider(B.class));
317        }
318    }