/* * Copyright 2013 Guidewire Software, Inc. */ package gw.config; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.module.IExecutionEnvironment; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import java.util.Stack; public abstract class ServiceKernel { private Map<Class<? extends IService>, IService> _services; private Stack<IService> _initingServices = new Stack<IService>(); private boolean _definingServices = false; protected ServiceKernel() { resetKernel(); } protected void resetKernel() { _services = new HashMap<Class<? extends IService>, IService>(); _definingServices = true; try { defineServices(); } finally { _definingServices = false; } redefineServices(); } /** * Contains all the definitions of the services provided by this kernel */ protected abstract void defineServices(); /** * Contains the redefinition logic for this kernel */ protected abstract void redefineServices(); /** * @param service * @return */ public <T extends IService> T getService( Class<? extends T> service ) { if( _definingServices ) { throw new IllegalStateException( "Service definition in progress, access to " + service.getName() + " is not " + "allowed. Move this access to the init() method of the offending service." ); } IService serviceImpl = _services.get( service ); if( serviceImpl == null ) { throw new IllegalStateException( "The service " + service.getName() + " is not provided by this ServiceKernel." ); } return (T)serviceImpl; } /** * Overrides the default implemenation of the service with a different provider. Note that the current * provider cannot have been accessed (all services must be consistent during runtime.) * * @param service - the service to provide * @param newProvider - the new provider of this service */ public <T extends IService, Q extends T> void redefineService(Class<? extends T> service, Q newProvider) { if( _definingServices ) { throw new IllegalStateException( "Service definition in progress, so service redefinition is not allowed. Please " + "move redefinitions to the redefineServices method." ); } IService existingServiceImpl = _services.get( service ); if( existingServiceImpl == null ) { throw new IllegalArgumentException( "Service " + service.getName() + " is not defined in this ServiceKernel."); } if( existingServiceImpl.isInited() ) { throw new IllegalStateException( "Service " + service.getName() + " has already been " + "initialized with the " + existingServiceImpl.getClass().getName() + " implementation"); } _services.put( service, newProvider ); } public <T extends IService, Q extends T> void redefineService_Privileged(Class<? extends T> service, Q newProvider) { IService existingServiceImpl = _services.get( service ); if( existingServiceImpl == null ) { throw new IllegalArgumentException( "Service " + service.getName() + " is not defined in this ServiceKernel."); } _services.put( service, newProvider ); } protected <T extends IService> void defineService(Class<? extends T> serviceClass, Class<? extends T> implClass ) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { Constructor ctor = implClass.getDeclaredConstructor(); ctor.setAccessible( true ); T serviceImpl = (T)ctor.newInstance(); defineService( serviceClass, serviceImpl ); } /** * Defines a service provided by this ServiceKernel * * @param service - the service to provide * @param defaultImplementation - the default implementation of this service */ protected <T extends IService, Q extends T> void defineService(Class<? extends T> service, Q defaultImplementation) { if( !_definingServices ) { throw new IllegalStateException( "Service definition must be done only in the defineServices() method." ); } if( !service.isInterface() ) { throw new IllegalArgumentException( "Services may only be defined as interfaces, and " + service.getName() + " is not an interface" ); } IService existingServiceImpl = _services.get( service ); if( existingServiceImpl != null ) { throw new IllegalStateException( "Service " + service.getName() + " has already been " + "defined with the " + existingServiceImpl.getClass().getName() + " default implementation"); } _services.put( service, defaultImplementation ); } /** * @param initClassName a class name of a class that implements {@link ServiceKernelInit) and that will be created * and given a chance to redefine the service implementations in this kernel. */ protected void redefineServicesWithClass( String initClassName ) { try { Class<?> aClass = getClass().forName( initClassName ); ServiceKernelInit init = (ServiceKernelInit)aClass.newInstance(); init.init( this ); } catch( ClassNotFoundException e ) { try { Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass( initClassName ); ServiceKernelInit init = (ServiceKernelInit)aClass.newInstance(); init.init( this ); } catch (Exception e1) { e1.printStackTrace(); throw new RuntimeException( e1 ); } } catch (Exception e1) { throw new RuntimeException( e1 ); } } private <T extends IService> void detectCircularInitializationDependencies( IService service ) { if( _initingServices.contains( service ) ) { StringBuilder sb = new StringBuilder( "Circular service initialization dependency detected : " ); for( IService initingService : _initingServices ) { sb.append( "\n\t" ).append( initingService ); } throw new IllegalStateException( sb.toString() ); } } }