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 */
016package org.kuali.rice.krad.data.provider;
017
018import org.junit.Before;
019import org.junit.Test;
020import org.kuali.rice.krad.data.provider.impl.ProviderRegistryImpl;
021
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collections;
025import java.util.List;
026import java.util.Random;
027import java.util.concurrent.BlockingQueue;
028import java.util.concurrent.Callable;
029import java.util.concurrent.ExecutorService;
030import java.util.concurrent.Executors;
031import java.util.concurrent.LinkedBlockingQueue;
032import java.util.concurrent.TimeUnit;
033
034import static junit.framework.Assert.assertEquals;
035import static org.junit.Assert.assertNull;
036import static org.junit.Assert.assertSame;
037import static org.mockito.Matchers.any;
038import static org.mockito.Matchers.eq;
039import static org.mockito.Mockito.mock;
040import static org.mockito.Mockito.when;
041
042/**
043 * Tests ProviderRegistryImpl
044 */
045public 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        final Random random = new Random(System.currentTimeMillis());
240
241        Callable<Object>[] producers = new Callable[providers];
242        Callable<Object>[] consumers = new Callable[providers];
243        Callable<Object> producer = new Callable<Object>() {
244            @Override
245            public Object call() throws Exception {
246                Provider p = mock(TYPES[random.nextInt(5)]);
247                registry.registerProvider(p);
248                queue.add(p);
249                return null;
250            }
251        };
252        Callable<Object> consumer = new Callable<Object>() {
253            @Override
254            public Object call() throws Exception {
255                Provider p = queue.take();
256                registry.unregisterProvider(p);
257                return null;
258            }
259        };
260
261        Arrays.fill(producers, producer);
262        Arrays.fill(consumers, consumer);
263
264        List<Callable<Object>> tasks = new ArrayList<Callable<Object>>(providers * 2);
265        tasks.addAll(Arrays.asList(producers));
266        tasks.addAll(Arrays.asList(consumers));
267        Collections.shuffle(tasks);
268
269        System.out.println("Registering and unregistering " + providers + " providers");
270        threadpool.invokeAll(tasks, 10, TimeUnit.SECONDS);
271
272        // all producers and consumers should have run, we should be back at 0 providers registered
273        assertEquals(0, registry.getProviders().size());
274    }
275
276    /**
277     * Tests registration and lookup of multiple PersistenceProviders
278     */
279    @Test
280    public void testRegisterPersistenceProviders() {
281        PersistenceProvider mockA = mock(PersistenceProvider.class);
282        when(mockA.handles(eq(TYPE_A))).thenReturn(true);
283
284        PersistenceProvider mockB = mock(PersistenceProvider.class);
285        when(mockB.handles(eq(TYPE_B))).thenReturn(true);
286
287        PersistenceProvider mockAB = mock(PersistenceProvider.class);
288        when(mockAB.handles(eq(TYPE_A))).thenReturn(true);
289        when(mockAB.handles(eq(TYPE_B))).thenReturn(true);
290
291        registry.registerProvider(mockA);
292
293        assertEquals(1, registry.getProviders().size());
294        assertEquals(0, registry.getMetadataProviders().size());
295        assertEquals(1, registry.getProvidersForType(PersistenceProvider.class).size());
296        assertEquals(0, registry.getProvidersForType(MetadataProvider.class).size());
297        assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
298        assertSame(mockA, registry.getPersistenceProvider(A.class));
299
300        registry.registerProvider(mockB);
301
302        assertEquals(2, registry.getProviders().size());
303        assertEquals(0, registry.getMetadataProviders().size());
304        assertEquals(2, registry.getProvidersForType(PersistenceProvider.class).size());
305        assertEquals(0, registry.getProvidersForType(MetadataProvider.class).size());
306        assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
307        assertSame(mockA, registry.getPersistenceProvider(A.class));
308        assertSame(mockB, registry.getPersistenceProvider(B.class));
309
310        registry.registerProvider(mockAB);
311
312        assertEquals(3, registry.getProviders().size());
313        assertEquals(0, registry.getMetadataProviders().size());
314        assertEquals(3, registry.getProvidersForType(PersistenceProvider.class).size());
315        assertEquals(0, registry.getProvidersForType(MetadataProvider.class).size());
316        assertEquals(0, registry.getProvidersForType(CustomProvider.class).size());
317        assertSame(mockA, registry.getPersistenceProvider(A.class)); // still returns mockA
318        assertSame(mockB, registry.getPersistenceProvider(B.class));
319    }
320}