/*
* Copyright Terracotta, Inc.
*
* Licensed 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.ehcache.core.internal.service;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.ehcache.core.EhcacheWithLoaderWriter;
import org.ehcache.core.spi.store.CacheProvider;
import org.ehcache.spi.service.ServiceProvider;
import org.ehcache.spi.loaderwriter.CacheLoaderWriterProvider;
import org.ehcache.spi.service.Service;
import org.ehcache.spi.service.ServiceConfiguration;
import org.ehcache.spi.service.ServiceDependencies;
import org.ehcache.core.spi.services.DefaultTestProvidedService;
import org.ehcache.core.spi.services.DefaultTestService;
import org.ehcache.core.spi.services.FancyCacheProvider;
import org.ehcache.core.spi.services.TestProvidedService;
import org.ehcache.core.spi.services.TestService;
import org.hamcrest.CoreMatchers;
import org.junit.Ignore;
import org.junit.Test;
import static org.ehcache.core.internal.service.ServiceLocator.dependencySet;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.withSettings;
/**
* Tests for {@link ServiceLocator}.
*/
public class ServiceLocatorTest {
@Test
public void testClassHierarchies() {
ServiceLocator.DependencySet dependencySet = dependencySet();
final Service service = new ChildTestService();
dependencySet.with(service);
assertThat(dependencySet.providerOf(FooProvider.class), sameInstance(service));
final Service fancyCacheProvider = new FancyCacheProvider();
dependencySet.with(fancyCacheProvider);
final Collection<CacheProvider> servicesOfType = dependencySet.providersOf(CacheProvider.class);
assertThat(servicesOfType, is(not(empty())));
assertThat(servicesOfType.iterator().next(), sameInstance(fancyCacheProvider));
}
@Test
public void testDoesNotUseTCCL() {
Thread.currentThread().setContextClassLoader(new ClassLoader() {
@Override
public Enumeration<URL> getResources(String name) throws IOException {
throw new AssertionError();
}
});
dependencySet().with(TestService.class).build().getService(TestService.class);
}
@Test
public void testAttemptsToStopStartedServicesOnInitFailure() {
Service s1 = new ParentTestService();
FancyCacheProvider s2 = new FancyCacheProvider();
ServiceLocator locator = dependencySet().with(s1).with(s2).build();
try {
locator.startAllServices();
fail();
} catch (Exception e) {
// see org.ehcache.spi.ParentTestService.start()
assertThat(e, instanceOf(RuntimeException.class));
assertThat(e.getMessage(), is("Implement me!"));
}
assertThat(s2.startStopCounter, is(0));
}
@Test
public void testAttemptsToStopAllServicesOnCloseFailure() {
Service s1 = mock(CacheProvider.class);
Service s2 = mock(FooProvider.class);
Service s3 = mock(CacheLoaderWriterProvider.class);
ServiceLocator locator = dependencySet().with(s1).with(s2).with(s3).build();
try {
locator.startAllServices();
} catch (Exception e) {
fail();
}
final RuntimeException thrown = new RuntimeException();
doThrow(thrown).when(s1).stop();
try {
locator.stopAllServices();
fail();
} catch (Exception e) {
assertThat(e, CoreMatchers.<Exception>sameInstance(thrown));
}
verify(s1).stop();
verify(s2).stop();
verify(s3).stop();
}
@Test
public void testStopAllServicesOnlyStopsEachServiceOnce() throws Exception {
Service s1 = mock(CacheProvider.class, withSettings().extraInterfaces(CacheLoaderWriterProvider.class));
ServiceLocator locator = dependencySet().with(s1).build();
try {
locator.startAllServices();
} catch (Exception e) {
fail();
}
locator.stopAllServices();
verify(s1, times(1)).stop();
}
@Test
public void testCanOverrideDefaultServiceFromServiceLoader() {
ServiceLocator locator = dependencySet().with(new ExtendedTestService()).build();
TestService testService = locator.getService(TestService.class);
assertThat(testService, instanceOf(ExtendedTestService.class));
}
@Test
public void testCanOverrideServiceDependencyWithoutOrderingProblem() throws Exception {
final AtomicBoolean started = new AtomicBoolean(false);
ServiceLocator serviceLocator = dependencySet().with(new TestServiceConsumerService())
.with(new TestService() {
@Override
public void start(ServiceProvider<Service> serviceProvider) {
started.set(true);
}
@Override
public void stop() {
// no-op
}
}).build();
serviceLocator.startAllServices();
assertThat(started.get(), is(true));
}
@Test
public void testServicesInstanciatedOnceAndStartedOnce() throws Exception {
@ServiceDependencies(TestProvidedService.class)
class Consumer1 implements Service {
@Override
public void start(ServiceProvider<Service> serviceProvider) {
}
@Override
public void stop() {
}
}
@ServiceDependencies(TestProvidedService.class)
class Consumer2 implements Service {
TestProvidedService testProvidedService;
@Override
public void start(ServiceProvider<Service> serviceProvider) {
testProvidedService = serviceProvider.getService(TestProvidedService.class);
}
@Override
public void stop() {
}
}
Consumer1 consumer1 = spy(new Consumer1());
Consumer2 consumer2 = new Consumer2();
ServiceLocator.DependencySet dependencySet = dependencySet();
// add some services
dependencySet.with(consumer1);
dependencySet.with(consumer2);
dependencySet.with(new TestService() {
@Override
public void start(ServiceProvider<Service> serviceProvider) {
}
@Override
public void stop() {
// no-op
}
});
// simulate what is done in ehcachemanager
dependencySet.with(TestService.class);
ServiceLocator serviceLocator = dependencySet.build();
serviceLocator.startAllServices();
serviceLocator.stopAllServices();
verify(consumer1, times(1)).start(serviceLocator);
verify(consumer1, times(1)).stop();
assertThat(consumer2.testProvidedService.ctors(), greaterThanOrEqualTo(1));
assertThat(consumer2.testProvidedService.stops(), equalTo(1));
assertThat(consumer2.testProvidedService.starts(), equalTo(1));
}
@Test
public void testRedefineDefaultServiceWhileDependingOnIt() throws Exception {
ServiceLocator serviceLocator = dependencySet().with(new YetAnotherCacheProvider()).build();
serviceLocator.startAllServices();
}
@Test
@Ignore
public void testCircularDeps() throws Exception {
final class StartStopCounter {
final AtomicInteger startCounter = new AtomicInteger(0);
final AtomicReference<ServiceProvider<Service>> startServiceProvider = new AtomicReference<ServiceProvider<Service>>();
final AtomicInteger stopCounter = new AtomicInteger(0);
public void countStart(ServiceProvider<Service> serviceProvider) {
startCounter.incrementAndGet();
startServiceProvider.set(serviceProvider);
}
public void countStop() {
stopCounter.incrementAndGet();
}
}
@ServiceDependencies(TestProvidedService.class)
class Consumer1 implements Service {
final StartStopCounter startStopCounter = new StartStopCounter();
@Override
public void start(ServiceProvider<Service> serviceProvider) {
assertThat(serviceProvider.getService(TestProvidedService.class), is(notNullValue()));
startStopCounter.countStart(serviceProvider);
}
@Override
public void stop() {
startStopCounter.countStop();
}
}
@ServiceDependencies(Consumer1.class)
class Consumer2 implements Service {
final StartStopCounter startStopCounter = new StartStopCounter();
@Override
public void start(ServiceProvider<Service> serviceProvider) {
assertThat(serviceProvider.getService(Consumer1.class), is(notNullValue()));
startStopCounter.countStart(serviceProvider);
}
@Override
public void stop() {
startStopCounter.countStop();
}
}
@ServiceDependencies(Consumer2.class)
class MyTestProvidedService extends DefaultTestProvidedService {
final StartStopCounter startStopCounter = new StartStopCounter();
@Override
public void start(ServiceProvider<Service> serviceProvider) {
assertThat(serviceProvider.getService(Consumer2.class), is(notNullValue()));
startStopCounter.countStart(serviceProvider);
super.start(serviceProvider);
}
@Override
public void stop() {
startStopCounter.countStop();
super.stop();
}
}
@ServiceDependencies(DependsOnMe.class)
class DependsOnMe implements Service {
final StartStopCounter startStopCounter = new StartStopCounter();
@Override
public void start(ServiceProvider<Service> serviceProvider) {
assertThat(serviceProvider.getService(DependsOnMe.class), sameInstance(this));
startStopCounter.countStart(serviceProvider);
}
@Override
public void stop() {
startStopCounter.countStop();
}
}
ServiceLocator.DependencySet dependencySet = dependencySet();
Consumer1 consumer1 = new Consumer1();
Consumer2 consumer2 = new Consumer2();
MyTestProvidedService myTestProvidedService = new MyTestProvidedService();
DependsOnMe dependsOnMe = new DependsOnMe();
// add some services
dependencySet.with(consumer1);
dependencySet.with(consumer2);
dependencySet.with(myTestProvidedService);
dependencySet.with(dependsOnMe);
ServiceLocator serviceLocator = dependencySet.build();
// simulate what is done in ehcachemanager
serviceLocator.startAllServices();
serviceLocator.stopAllServices();
assertThat(consumer1.startStopCounter.startCounter.get(), is(1));
assertThat(consumer1.startStopCounter.startServiceProvider.get(), CoreMatchers.<ServiceProvider<Service>>is(serviceLocator));
assertThat(consumer2.startStopCounter.startCounter.get(), is(1));
assertThat(consumer2.startStopCounter.startServiceProvider.get(), CoreMatchers.<ServiceProvider<Service>>is(serviceLocator));
assertThat(myTestProvidedService.startStopCounter.startCounter.get(), is(1));
assertThat(myTestProvidedService.startStopCounter.startServiceProvider.get(), CoreMatchers.<ServiceProvider<Service>>is(serviceLocator));
assertThat(dependsOnMe.startStopCounter.startCounter.get(), is(1));
assertThat(dependsOnMe.startStopCounter.startServiceProvider.get(), CoreMatchers.<ServiceProvider<Service>>is(serviceLocator));
assertThat(consumer1.startStopCounter.stopCounter.get(), is(1));
assertThat(consumer2.startStopCounter.stopCounter.get(), is(1));
assertThat(myTestProvidedService.startStopCounter.stopCounter.get(), is(1));
assertThat(dependsOnMe.startStopCounter.stopCounter.get(), is(1));
}
}
@ServiceDependencies(FancyCacheProvider.class)
class YetAnotherCacheProvider implements CacheProvider {
@Override
public <K, V> EhcacheWithLoaderWriter<K, V> createCache(Class<K> keyClazz, Class<V> valueClazz, ServiceConfiguration<?>... config) {
return null;
}
@Override
public void releaseCache(EhcacheWithLoaderWriter<?, ?> resource) {
// no-op
}
@Override
public void start(ServiceProvider<Service> serviceProvider) {
// no-op
}
@Override
public void stop() {
// no-op
}
}
class ExtendedTestService extends DefaultTestService {
}
interface FooProvider extends Service {
}
@ServiceDependencies(TestService.class)
class TestServiceConsumerService implements Service {
@Override
public void start(ServiceProvider<Service> serviceProvider) {
assertThat(serviceProvider.getService(TestService.class), notNullValue());
}
@Override
public void stop() {
// no-op
}
}
class ParentTestService implements FooProvider {
@Override
public void start(final ServiceProvider<Service> serviceProvider) {
throw new UnsupportedOperationException("Implement me!");
}
@Override
public void stop() { }
}
class ChildTestService extends ParentTestService {
@Override
public void start(final ServiceProvider<Service> serviceProvider) {
throw new UnsupportedOperationException("Implement me!");
}
}