/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.service.servicemanager; import com.foundationdb.server.service.servicemanager.configuration.DefaultServiceConfigurationHandler; import com.foundationdb.util.JUnitUtils; import com.google.inject.Module; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public final class GuiceInjectionTester { public <I> GuiceInjectionTester bind(Class<I> anInterface, Class<? extends I> anImplementation) { configHandler.bind(anInterface.getName(), anImplementation.getName(), null); return this; } public <I extends ServiceManagerBase> GuiceInjectionTester manager(Class<I> serviceManagerInterfaceClass, I serviceManager) { this.serviceManagerInterfaceClass = serviceManagerInterfaceClass; this.serviceManager = serviceManager; return this; } public <I> GuiceInjectionTester prioritize(Class<I> anInterface) { configHandler.prioritize(anInterface.getName()); return this; } @SuppressWarnings("unchecked") public GuiceInjectionTester startAndStop(Class<?>... requiredClasses) { for (Class<?> requiredClass : requiredClasses) { configHandler.require(requiredClass.getName()); } try { guicer = Guicer.forServices((Class<ServiceManagerBase>)serviceManagerInterfaceClass, serviceManager, configHandler.serviceBindings(true), configHandler.priorities(), Collections.<Module>emptyList()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } for (Class<?> requiredClass : guicer.directlyRequiredClasses()) { guicer.get(requiredClass, shutdownHook); } guicer.stopAllServices(shutdownHook); return this; } public GuiceInjectionTester checkDependencies(Class<?> aClass, Class<?>... itsDependencies) { for (Class<?> dependency : itsDependencies) { checkSingleDependency(aClass, dependency); } // alternate method List<Class<?>> allClassesExpected = new ArrayList<>(); allClassesExpected.add(aClass); Collections.addAll(allClassesExpected, itsDependencies); List<Class<?>> allClassesActual = new ArrayList<>(); for (Object instance : guicer.dependenciesFor(aClass)) { allClassesActual.add(instance.getClass()); } JUnitUtils.equalCollections("for " + aClass, allClassesExpected, allClassesActual); return this; } public GuiceInjectionTester check(Class<?>... expectedClasses) { List<Class<?>> expectedList = Arrays.asList(expectedClasses); checkExactContents("shutdown", expectedList, shutdownHook.stoppedClasses()); return this; } private void checkExactContents(String whichList, List<Class<?>> expectedList, List<Class<?>> actualList) { if (expectedList.size() != actualList.size()) { JUnitUtils.equalCollections(whichList + " lists not of same size", expectedList, actualList); } assertEquals(whichList + " size", expectedList.size(), actualList.size()); assertTrue(whichList + ": " + l(expectedList) + " != " + l(actualList), actualList.containsAll(expectedList)); } private void checkSingleDependency(Class<?> aClass, Class<?> itsDependency) { // The class should appear after the dependency in startup, and before it for shutdown List<Class<?>> shutdownOrder = shutdownHook.stoppedClasses(); int aClassShutdownOrder = findInList(aClass, shutdownOrder); int dependencyShutdownOrder = findInList(itsDependency, shutdownOrder); assertTrue( String.format("%s stopped before %s: %s", n(itsDependency), n(aClass), l(shutdownOrder)), aClassShutdownOrder < dependencyShutdownOrder ); } private static String n(Class<?> aClass) { return aClass.getSimpleName(); } private List<String> l(List<Class<?>> list) { List<String> simpleNames = new ArrayList<>(); for (Class<?> aClass : list) { simpleNames.add(n(aClass)); } return simpleNames; } private int findInList(Class<?> aClass, List<Class<?>> list) { int index = list.indexOf(aClass); assertTrue(aClass + " not in list " + list, index >= 0); return index; } private final DefaultServiceConfigurationHandler configHandler = new DefaultServiceConfigurationHandler(); private final ListOnShutdown shutdownHook = new ListOnShutdown(); private Class<? extends ServiceManagerBase> serviceManagerInterfaceClass; private ServiceManagerBase serviceManager; private Guicer guicer; // nested classes private static class ListOnShutdown implements Guicer.ServiceLifecycleActions<Object> { public List<Class<?>> stoppedClasses() { return stoppedClasses; } @Override public void onStart(Object service) { } @Override public void onShutdown(Object service) { stoppedClasses.add(service.getClass()); } @Override public Object castIfActionable(Object object) { return object; } private final List<Class<?>> stoppedClasses = new ArrayList<>(); } }