/*
* 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 org.ehcache.spi.service.ServiceProvider;
import org.ehcache.spi.service.PluralService;
import org.ehcache.spi.service.Service;
import org.ehcache.spi.service.ServiceDependencies;
import org.hamcrest.Matchers;
import org.junit.Test;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static org.ehcache.core.internal.service.ServiceLocator.dependencySet;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.isOneOf;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* Tests handling of {@link PluralService} by {@link ServiceLocator}.
*/
public class ServiceLocatorPluralTest {
/**
* Ensures that multiple instances of a single {@code Service} implementation <i>without</i>
* the {@link PluralService} annotation can not have more than one instance registered.
*/
@Test
public void testMultipleInstanceRegistration() throws Exception {
final ServiceLocator.DependencySet serviceLocator = dependencySet();
final ConcreteService firstSingleton = new ConcreteService();
final ConcreteService secondSingleton = new ConcreteService();
serviceLocator.with(firstSingleton);
assertThat(serviceLocator.providersOf(ConcreteService.class), contains(firstSingleton));
assertThat(serviceLocator.providersOf(AdditionalService.class), Matchers.<AdditionalService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(AggregateService.class), Matchers.<AggregateService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(FooService.class), Matchers.<FooService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(BarService.class), Matchers.<BarService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(FoundationService.class), Matchers.<FoundationService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(AugmentedService.class), Matchers.<AugmentedService>contains(firstSingleton));
try {
serviceLocator.with(secondSingleton);
fail();
} catch (IllegalStateException e) {
// expected
assertThat(e.getMessage(), containsString(ConcreteService.class.getName()));
}
}
/**
* Ensures that multiple {@code Service} implementations of an interface <i>without</i> the
* {@link PluralService} annotation can not both be registered.
*/
@Test
public void testMultipleImplementationRegistration() throws Exception {
final ServiceLocator.DependencySet serviceLocator = dependencySet();
final ConcreteService firstSingleton = new ConcreteService();
final ExtraConcreteService secondSingleton = new ExtraConcreteService();
serviceLocator.with(firstSingleton);
assertThat(serviceLocator.providersOf(ConcreteService.class), contains(firstSingleton));
assertThat(serviceLocator.providersOf(AdditionalService.class), Matchers.<AdditionalService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(AggregateService.class), Matchers.<AggregateService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(FooService.class), Matchers.<FooService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(BarService.class), Matchers.<BarService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(FoundationService.class), Matchers.<FoundationService>contains(firstSingleton));
assertThat(serviceLocator.providersOf(AugmentedService.class), Matchers.<AugmentedService>contains(firstSingleton));
try {
serviceLocator.with(secondSingleton);
fail();
} catch (IllegalStateException e) {
// expected
final String message = e.getMessage();
assertThat(message, containsString(AdditionalService.class.getName()));
assertThat(message, containsString(AggregateService.class.getName()));
assertThat(message, containsString(FooService.class.getName()));
assertThat(message, containsString(BarService.class.getName()));
assertThat(message, containsString(FoundationService.class.getName()));
assertThat(message, containsString(AugmentedService.class.getName()));
}
}
/**
* Ensures that multiple {@code Service} implementations of an interface <i>with</i> the
* {@link PluralService} annotation can both be registered.
*/
@Test
public void testPluralRegistration() throws Exception {
final ServiceLocator.DependencySet dependencySet = dependencySet();
final AlphaServiceProviderImpl alphaServiceProvider = new AlphaServiceProviderImpl();
final BetaServiceProviderImpl betaServiceProvider = new BetaServiceProviderImpl();
dependencySet.with(alphaServiceProvider);
assertThat(dependencySet.providersOf(AlphaServiceProviderImpl.class),
everyItem(isOneOf(alphaServiceProvider)));
assertThat(dependencySet.providersOf(AlphaServiceProvider.class),
everyItem(Matchers.<AlphaServiceProvider>isOneOf(alphaServiceProvider)));
assertThat(dependencySet.providersOf(PluralServiceProvider.class),
everyItem(Matchers.<PluralServiceProvider>isOneOf(alphaServiceProvider)));
dependencySet.with(betaServiceProvider);
assertThat(dependencySet.providersOf(BetaServiceProviderImpl.class),
everyItem(isOneOf(betaServiceProvider)));
assertThat(dependencySet.providersOf(BetaServiceProvider.class),
everyItem(Matchers.<BetaServiceProvider>isOneOf(betaServiceProvider)));
assertThat(dependencySet.providersOf(PluralServiceProvider.class),
everyItem(Matchers.<PluralServiceProvider>isOneOf(alphaServiceProvider, betaServiceProvider)));
}
}
class StartStopCounter {
private AtomicInteger startCounter = new AtomicInteger(0);
private AtomicInteger stopCounter = new AtomicInteger(0);
private AtomicReference<ServiceProvider<Service>> serviceProvider = new AtomicReference<ServiceProvider<Service>>();
public void countStart(final ServiceProvider<Service> serviceProvider) {
this.startCounter.incrementAndGet();
this.serviceProvider.set(serviceProvider);
}
public void countStop() {
this.stopCounter.incrementAndGet();
}
public int getStartCounter() {
return startCounter.get();
}
public int getStopCounter() {
return stopCounter.get();
}
public ServiceProvider<Service> getServiceProvider() {
return serviceProvider.get();
}
}
@PluralService
interface PluralServiceProvider extends Service {
}
/**
* Top-level, concrete test class; multiples <b>not</b> permitted.
* <p>
* Inherits from
* <pre>{@code
* <- AbstractService @ServiceDependencies({ BetaService })
* => @ServiceDependencies({ BetaServiceProvider })
* <- CoreService @ServiceDependencies({ AlphaService })
* => @ServiceDependencies({ AlphaServiceProvider })
* <- BaseService
* <- FoundationService @ServiceDependencies({ InitialService, FoundationService.Provider })
* <- Service
* <- AugmentedService
* <- AggregateService
* <- FooService @ServiceDependencies({ FooService.Provider })
* <- Service
* <- BarService @ServiceDependencies({ BarService.Provider })
* <- Service
* <- AdditionalService @ServiceDependencies({ InitialService })
* <- Service
* }</pre>
*/
class ConcreteService extends AbstractService {
}
class ExtraConcreteService extends AbstractService {
}
@ServiceDependencies({ BetaService.class })
class AbstractService extends CoreService
implements AugmentedService {
}
@ServiceDependencies({ AlphaService.class })
class CoreService extends BaseService implements FoundationService {
}
// This class must *not* be annotated with @PluralService!
class BaseService implements Service {
final StartStopCounter counter = new StartStopCounter();
@Override
public void start(final ServiceProvider<Service> serviceProvider) {
this.counter.countStart(serviceProvider);
}
@Override
public void stop() {
this.counter.countStop();
}
}
interface AugmentedService extends AdditionalService, AggregateService {
}
@ServiceDependencies({ InitialService.class })
interface AdditionalService extends Service {
}
@ServiceDependencies({ InitialService.class, FoundationService.Provider.class })
interface FoundationService extends Service {
interface Provider extends Service {
}
}
interface AggregateService extends FooService, BarService {
}
@ServiceDependencies({ FooService.Provider.class })
interface FooService extends Service {
interface Provider extends Service {
}
}
@ServiceDependencies({ BarService.Provider.class })
interface BarService extends Service {
interface Provider extends Service {
}
}
@ServiceDependencies({ AlphaServiceProvider.class })
interface AlphaService extends Service, NonServiceAlpha {
}
@ServiceDependencies({ BetaServiceProvider.class })
interface BetaService extends Service, NonServiceBeta {
}
interface InitialService extends Service {
}
interface AlphaServiceProvider extends PluralServiceProvider {
}
class AlphaServiceProviderImpl extends BaseService implements AlphaServiceProvider {
}
interface BetaServiceProvider extends PluralServiceProvider {
}
class BetaServiceProviderImpl extends BaseService implements BetaServiceProvider {
}
interface NonServiceAlpha {
}
interface NonServiceBeta {
}
@ServiceDependencies({ AlphaService.class })
class NotAService extends AlsoNotAService {
}
@ServiceDependencies({ BetaService.class })
class AlsoNotAService implements NotAServiceInterface {
}
interface NotAServiceInterface {
}
interface NonService {
}