/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.service;
import java.util.Map;
import com.google.common.collect.ImmutableClassToInstanceMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.opengamma.util.ArgumentChecker;
/**
* Registry containing service-providing objects keyed by class.
* <p>
* This is used via {@code ThreadLocalServiceContext} to provide access to services
* across the whole system.
*/
public final class ServiceContext {
/**
* The services held by this context.
*/
private ImmutableClassToInstanceMap<Object> _services;
//-------------------------------------------------------------------------
/**
* Creates a new service context using the provided map to populate the service registry.
*
* @param services a map of type to service-providing objects, not null
* @return a populated service context, not null
*/
public static ServiceContext of(Map<Class<?>, Object> services) {
ArgumentChecker.noNulls(services, "services");
return new ServiceContext(ImmutableClassToInstanceMap.copyOf(services));
}
/**
* Creates a new service context using the single provided service and type.
* Typically this context would then be augmented using the {@code with} method.
*
* @param <T> the type being added
* @param clazz the class of the initial service being registered, not null
* @param service a service-providing object, not null
* @return a populated service context, not null
*/
public static <T> ServiceContext of(Class<T> clazz, T service) {
ArgumentChecker.notNull(clazz, "class");
ArgumentChecker.notNull(service, "service");
return new ServiceContext(ImmutableClassToInstanceMap.builder().put(clazz, service).build());
}
//-------------------------------------------------------------------------
/**
* Restricted constructor.
*
* @param services the services to start with, not null
*/
private ServiceContext(ImmutableClassToInstanceMap<Object> services) {
// don't need to argument check as private
_services = services;
}
//-------------------------------------------------------------------------
/**
* Checks if the service-providing object is available for the specified type.
*
* @param serviceClass the class of the service, not null
* @return true if the service is available
*/
public boolean contains(Class<?> serviceClass) {
ArgumentChecker.notNull(serviceClass, "serviceClass");
return _services.containsKey(serviceClass);
}
/**
* Gets the service-providing object of the specified type.
*
* @param <T> expected type
* @param serviceClass the class of the service, not null
* @return the service-providing object, not null
* @throws IllegalArgumentException if the service is not found
*/
public <T> T get(Class<T> serviceClass) {
ArgumentChecker.notNull(serviceClass, "serviceClass");
final T service = _services.getInstance(serviceClass);
if (service == null) {
throw new IllegalArgumentException("No service found: " + serviceClass);
}
return service;
}
//-------------------------------------------------------------------------
/**
* Returns a copy of this context with the map of services added.
* <p>
* If any services are provided that are already registered, the service registry
* will be updated with the provided services.
*
* @param services a map of services objects keyed by their class, not null
* @return an updated service context
*/
public ServiceContext with(Map<Class<?>, Object> services) {
// We have to calculate which of the original objects need to be
// retained as ImmutableMap.Builder won't allow a key to be put
// more than once
ArgumentChecker.noNulls(services, "services");
Map<Class<?>, Object> unchanged = Maps.difference(_services, services).entriesOnlyOnLeft();
ImmutableClassToInstanceMap<Object> combined = ImmutableClassToInstanceMap.builder()
.putAll(services)
.putAll(unchanged)
.build();
return new ServiceContext(combined);
}
/**
* Returns a copy of this context with the specified service added.
* <p>
* If the service provided is already registered, the service registry
* will be updated with the provided service.
*
* @param clazz the class of the service to be added, not null
* @param service the service-providing object to be registered, not null
* @return an updated service context
*/
public ServiceContext with(Class<?> clazz, Object service) {
ArgumentChecker.notNull(clazz, "class");
ArgumentChecker.notNull(service, "service");
return with(ImmutableMap.<Class<?>, Object>of(clazz, service));
}
//-------------------------------------------------------------------------
@Override
public String toString() {
return "ServiceContext[size=" + _services.size() + "]";
}
}