| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.sling.i18n.impl; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.mockito.Matchers.any; |
| import static org.mockito.Matchers.eq; |
| import static org.mockito.Mockito.times; |
| import static org.powermock.api.mockito.PowerMockito.doReturn; |
| import static org.powermock.api.mockito.PowerMockito.spy; |
| import static org.powermock.api.mockito.PowerMockito.verifyPrivate; |
| |
| import java.lang.annotation.Annotation; |
| import java.util.Locale; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.sling.i18n.impl.JcrResourceBundleProvider.Key; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Mock; |
| import org.mockito.Mockito; |
| import org.osgi.framework.BundleContext; |
| import org.powermock.api.mockito.PowerMockito; |
| import org.powermock.core.classloader.annotations.PrepareForTest; |
| import org.powermock.modules.junit4.PowerMockRunner; |
| |
| /** |
| * Test case to verify that each bundle is only loaded once, even |
| * if concurrent requests for the same bundle are made. |
| */ |
| @RunWith(PowerMockRunner.class) |
| @PrepareForTest(JcrResourceBundleProvider.class) |
| public class ConcurrentJcrResourceBundleLoadingTest { |
| |
| @Mock JcrResourceBundle english; |
| @Mock JcrResourceBundle german; |
| |
| private JcrResourceBundleProvider provider; |
| |
| @Before |
| public void setup() throws Exception { |
| provider = spy(new JcrResourceBundleProvider()); |
| provider.activate(PowerMockito.mock(BundleContext.class), new JcrResourceBundleProvider.Config() { |
| |
| @Override |
| public Class<? extends Annotation> annotationType() { |
| return JcrResourceBundleProvider.Config.class; |
| } |
| |
| @Override |
| public boolean preload_bundles() { |
| return false; |
| } |
| |
| @Override |
| public String locale_default() { |
| return "en"; |
| } |
| |
| @Override |
| public long invalidation_delay() { |
| return 5000; |
| } |
| }); |
| doReturn(english).when(provider, "createResourceBundle", eq(null), eq(Locale.ENGLISH)); |
| doReturn(german).when(provider, "createResourceBundle", eq(null), eq(Locale.GERMAN)); |
| Mockito.when(german.getLocale()).thenReturn(Locale.GERMAN); |
| Mockito.when(english.getLocale()).thenReturn(Locale.ENGLISH); |
| Mockito.when(german.getParent()).thenReturn(english); |
| } |
| |
| @Test |
| public void loadBundlesOnlyOncePerLocale() throws Exception { |
| assertEquals(english, provider.getResourceBundle(Locale.ENGLISH)); |
| assertEquals(english, provider.getResourceBundle(Locale.ENGLISH)); |
| assertEquals(german, provider.getResourceBundle(Locale.GERMAN)); |
| assertEquals(german, provider.getResourceBundle(Locale.GERMAN)); |
| |
| verifyPrivate(provider, times(2)).invoke("createResourceBundle", eq(null), any(Locale.class)); |
| } |
| |
| @Test |
| public void loadBundlesOnlyOnceWithConcurrentRequests() throws Exception { |
| final int numberOfThreads = 40; |
| final ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads / 2); |
| for (int i = 0; i < numberOfThreads; i++) { |
| final Locale language = i < numberOfThreads / 2 ? Locale.ENGLISH : Locale.GERMAN; |
| executor.submit(new Runnable() { |
| @Override |
| public void run() { |
| provider.getResourceBundle(language); |
| } |
| }); |
| } |
| executor.shutdown(); |
| executor.awaitTermination(5, TimeUnit.SECONDS); |
| |
| verifyPrivate(provider, times(1)).invoke("createResourceBundle", eq(null), eq(Locale.ENGLISH)); |
| verifyPrivate(provider, times(1)).invoke("createResourceBundle", eq(null), eq(Locale.GERMAN)); |
| } |
| |
| @Test |
| public void newBundleUsedAfterReload() throws Exception { |
| provider.getResourceBundle(Locale.ENGLISH); |
| provider.getResourceBundle(Locale.GERMAN); |
| |
| // reloading german should not reload any other bundle |
| provider.reloadBundle(new Key(null, Locale.GERMAN)); |
| provider.getResourceBundle(Locale.ENGLISH); |
| provider.getResourceBundle(Locale.GERMAN); |
| provider.getResourceBundle(Locale.ENGLISH); |
| provider.getResourceBundle(Locale.GERMAN); |
| provider.getResourceBundle(Locale.ENGLISH); |
| provider.getResourceBundle(Locale.GERMAN); |
| |
| verifyPrivate(provider, times(1)).invoke("createResourceBundle", eq(null), eq(Locale.ENGLISH)); |
| verifyPrivate(provider, times(2)).invoke("createResourceBundle", eq(null), eq(Locale.GERMAN)); |
| } |
| |
| @Test |
| public void newBundleUsedAsParentAfterReload() throws Exception { |
| provider.getResourceBundle(Locale.ENGLISH); |
| provider.getResourceBundle(Locale.GERMAN); |
| |
| // reloading english should also reload german (because it has english as a parent) |
| provider.reloadBundle(new Key(null, Locale.ENGLISH)); |
| provider.getResourceBundle(Locale.ENGLISH); |
| provider.getResourceBundle(Locale.GERMAN); |
| provider.getResourceBundle(Locale.ENGLISH); |
| provider.getResourceBundle(Locale.GERMAN); |
| provider.getResourceBundle(Locale.ENGLISH); |
| provider.getResourceBundle(Locale.GERMAN); |
| |
| verifyPrivate(provider, times(2)).invoke("createResourceBundle", eq(null), eq(Locale.ENGLISH)); |
| verifyPrivate(provider, times(2)).invoke("createResourceBundle", eq(null), eq(Locale.GERMAN)); |
| } |
| } |