use of com.google.inject.spi.Message in project guice by google.
the class SingletonScope method scope.
/**
* Provides singleton scope with the following properties:
*
* <ul>
* <li>creates no more than one instance per Key as a creator is used no more than once
* <li>result is cached and returned quickly on subsequent calls
* <li>exception in a creator is not treated as instance creation and is not cached
* <li>creates singletons in parallel whenever possible
* <li>waits for dependent singletons to be created even across threads and when dependencies
* are shared as long as no circular dependencies are detected
* <li>returns circular proxy only when circular dependencies are detected
* <li>aside from that, blocking synchronization is only used for proxy creation and
* initialization
* </ul>
*
* @see CycleDetectingLockFactory
*/
@Override
public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
/**
* Locking strategy:
*/
return new Provider<T>() {
/**
* The lazily initialized singleton instance. Once set, this will either have type T or will
* be equal to NULL. Would never be reset to null.
*
* <p>Locking strategy: double-checked locking for quick exit when scope is initialized.
*/
volatile Object instance;
/**
* Circular proxies are used when potential deadlocks are detected. Guarded by itself.
* ConstructionContext is not thread-safe, so each call should be synchronized.
*
* <p>Locking strategy: manipulations with proxies list or instance initialization.
*/
final ConstructionContext<T> constructionContext = new ConstructionContext<>();
/**
* For each binding there is a separate lock that we hold during object creation.
*
* <p>Locking strategy: singleton instance creation.
*
* <ul>
* <li>allows to guarantee only one instance per singleton,
* <li>special type of a lock, that prevents potential deadlocks,
* <li>guards constructionContext for all operations except proxy creation
* </ul>
*/
final CycleDetectingLock<Key<?>> creationLock = cycleDetectingLockFactory.create(key);
/**
* The singleton provider needs a reference back to the injector, in order to get ahold of
* InternalContext during instantiation.
*/
@Nullable
final InjectorImpl injector;
{
// If we are getting called by Scoping
if (creator instanceof ProviderToInternalFactoryAdapter) {
injector = ((ProviderToInternalFactoryAdapter) creator).getInjector();
} else {
injector = null;
}
}
@SuppressWarnings("DoubleCheckedLocking")
@Override
public T get() {
// cache volatile variable for the usual case of already initialized object
final Object initialInstance = instance;
if (initialInstance == null) {
// instance is not initialized yet
// first, store the current InternalContext in a map, so that if there is a circular
// dependency error, we can use the InternalContext objects to create a complete
// error message.
// Handle injector being null, which can happen when users call Scoping.scope themselves
final InternalContext context = injector == null ? null : injector.getLocalContext();
// acquire lock for current binding to initialize an instance
final ListMultimap<Thread, Key<?>> locksCycle = creationLock.lockOrDetectPotentialLocksCycle();
if (locksCycle.isEmpty()) {
// this thread now owns creation of an instance
try {
// intentionally reread volatile variable to prevent double initialization
if (instance == null) {
// creator throwing an exception can cause circular proxies created in
// different thread to never be resolved, just a warning
T provided = creator.get();
Object providedNotNull = provided == null ? NULL : provided;
// scope called recursively can initialize instance as a side effect
if (instance == null) {
// detection within the same thread; they are not real instances to cache
if (Scopes.isCircularProxy(provided)) {
return provided;
}
synchronized (constructionContext) {
// guarantee thread-safety for instance and proxies initialization
instance = providedNotNull;
constructionContext.setProxyDelegates(provided);
}
} else {
// safety assert in case instance was initialized
Preconditions.checkState(instance == providedNotNull, "Singleton is called recursively returning different results");
}
}
} catch (RuntimeException e) {
// this helps to prevent potential memory leaks in circular proxies list
synchronized (constructionContext) {
constructionContext.finishConstruction();
}
throw e;
} finally {
// always release our creation lock, even on failures
creationLock.unlock();
}
} else {
if (context == null) {
throw new ProvisionException(ImmutableList.of(createCycleDependenciesMessage(locksCycle, null)));
}
// potential deadlock detected, creation lock is not taken by this thread
synchronized (constructionContext) {
// guarantee thread-safety for instance and proxies initialization
if (instance == null) {
// creating a proxy to satisfy circular dependency across several threads
Dependency<?> dependency = Preconditions.checkNotNull(context.getDependency(), "internalContext.getDependency()");
Class<?> rawType = dependency.getKey().getTypeLiteral().getRawType();
try {
@SuppressWarnings("unchecked") T proxy = (T) constructionContext.createProxy(context.getInjectorOptions(), rawType);
return proxy;
} catch (InternalProvisionException e) {
// best effort to create a rich error message
Message proxyCreationError = Iterables.getOnlyElement(e.getErrors());
Message cycleDependenciesMessage = createCycleDependenciesMessage(locksCycle, proxyCreationError);
// adding stack trace generated by us in addition to a standard one
throw new ProvisionException(ImmutableList.of(cycleDependenciesMessage, proxyCreationError));
}
}
}
}
// at this point we're sure that singleton was initialized,
// reread volatile variable to catch all corner cases
// caching volatile variable to minimize number of reads performed
final Object initializedInstance = instance;
Preconditions.checkState(initializedInstance != null, "Internal error: Singleton is not initialized contrary to our expectations");
@SuppressWarnings("unchecked") T initializedTypedInstance = (T) initializedInstance;
return initializedInstance == NULL ? null : initializedTypedInstance;
} else {
// singleton is already initialized and local cache can be used
@SuppressWarnings("unchecked") T typedInitialIntance = (T) initialInstance;
return initialInstance == NULL ? null : typedInitialIntance;
}
}
/**
* Helper method to create beautiful and rich error descriptions. Best effort and slow. Tries
* its best to provide dependency information from injectors currently available in a global
* internal context.
*
* <p>The main thing being done is creating a list of Dependencies involved into lock cycle
* across all the threads involved. This is a structure we're creating:
*
* <pre>
* { Current Thread, C.class, B.class, Other Thread, B.class, C.class, Current Thread }
* To be inserted in the beginning by Guice: { A.class, B.class, C.class }
* </pre>
*
* When we're calling Guice to create A and it fails in the deadlock while trying to create C,
* which is being created by another thread, which waits for B. List would be reversed before
* printing it to the end user.
*/
private Message createCycleDependenciesMessage(ListMultimap<Thread, Key<?>> locksCycle, @Nullable Message proxyCreationError) {
// this is the main thing that we'll show in an error message,
// current thread is populate by Guice
StringBuilder sb = new StringBuilder();
Formatter fmt = new Formatter(sb);
fmt.format("Encountered circular dependency spanning several threads.");
if (proxyCreationError != null) {
fmt.format(" %s", proxyCreationError.getMessage());
}
fmt.format("%n");
for (Thread lockedThread : locksCycle.keySet()) {
List<Key<?>> lockedKeys = locksCycle.get(lockedThread);
fmt.format("%s is holding locks the following singletons in the cycle:%n", lockedThread);
for (Key<?> lockedKey : lockedKeys) {
fmt.format("%s%n", Errors.convert(lockedKey));
}
for (StackTraceElement traceElement : lockedThread.getStackTrace()) {
fmt.format("\tat %s%n", traceElement);
}
}
fmt.close();
return new Message(Thread.currentThread(), sb.toString());
}
@Override
public String toString() {
return String.format("%s[%s]", creator, Scopes.SINGLETON);
}
};
}
use of com.google.inject.spi.Message in project guice by google.
the class ProviderMethodsModule method createProviderMethod.
private <T> ProviderMethod<T> createProviderMethod(Binder binder, Method method, Annotation annotation) {
binder = binder.withSource(method);
Errors errors = new Errors(method);
// prepare the parameter providers
InjectionPoint point = InjectionPoint.forMethod(method, typeLiteral);
// Define T as the method's return type.
@SuppressWarnings("unchecked") TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method);
Key<T> key = getKey(errors, returnType, method, method.getAnnotations());
boolean prepareMethodError = false;
try {
key = scanner.prepareMethod(binder, annotation, key, point);
} catch (Throwable t) {
prepareMethodError = true;
binder.addError(t);
}
if (Modifier.isAbstract(method.getModifiers())) {
checkState(prepareMethodError || key == null, "%s returned a non-null key (%s) for %s. prepareMethod() must return null for abstract" + " methods", scanner, key, method);
return null;
}
if (key == null) {
// scanner returned null. Skipping the binding.
return null;
}
Class<? extends Annotation> scopeAnnotation = Annotations.findScopeAnnotation(errors, method.getAnnotations());
for (Message message : errors.getMessages()) {
binder.addError(message);
}
return ProviderMethod.create(key, method, isStaticModule() || Modifier.isStatic(method.getModifiers()) ? null : delegate, ImmutableSet.copyOf(point.getDependencies()), scopeAnnotation, skipFastClassGeneration, annotation);
}
use of com.google.inject.spi.Message in project guice by google.
the class Messages method getOnlyCause.
/**
* Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
* zero or multiple messages with causes, null is returned.
*/
public static Throwable getOnlyCause(Collection<Message> messages) {
Throwable onlyCause = null;
for (Message message : messages) {
Throwable messageCause = message.getCause();
if (messageCause == null) {
continue;
}
if (onlyCause != null && !ThrowableEquivalence.INSTANCE.equivalent(onlyCause, messageCause)) {
return null;
}
onlyCause = messageCause;
}
return onlyCause;
}
use of com.google.inject.spi.Message in project druid by druid-io.
the class JsonConfigurator method configurate.
public <T> T configurate(Properties props, String propertyPrefix, Class<T> clazz, @Nullable Class<? extends T> defaultClass) throws ProvisionException {
verifyClazzIsConfigurable(jsonMapper, clazz, defaultClass);
// Make it end with a period so we only include properties with sub-object thingies.
final String propertyBase = propertyPrefix.endsWith(".") ? propertyPrefix : propertyPrefix + ".";
Map<String, Object> jsonMap = new HashMap<>();
for (String prop : props.stringPropertyNames()) {
if (prop.startsWith(propertyBase)) {
final String propValue = props.getProperty(prop);
Object value;
try {
// If it's a String Jackson wants it to be quoted, so check if it's not an object or array and quote.
String modifiedPropValue = propValue;
if (!(modifiedPropValue.startsWith("[") || modifiedPropValue.startsWith("{"))) {
modifiedPropValue = jsonMapper.writeValueAsString(propValue);
}
value = jsonMapper.readValue(modifiedPropValue, Object.class);
} catch (IOException e) {
// Do not log exception message or the property value as it might
// expose sensitive information
log.info("Unable to parse value of property [%s] as a json object, using as is.", prop);
value = propValue;
}
hieraricalPutValue(propertyPrefix, prop, prop.substring(propertyBase.length()), value, jsonMap);
}
}
final T config;
try {
if (defaultClass != null && jsonMap.isEmpty()) {
// No configs were provided. Don't use the jsonMapper; instead create a default instance of the default class
// using the no-arg constructor. We know it exists because verifyClazzIsConfigurable checks for it.
config = defaultClass.getConstructor().newInstance();
} else {
config = jsonMapper.convertValue(jsonMap, clazz);
}
} catch (IllegalArgumentException e) {
throw new ProvisionException(StringUtils.format("Problem parsing object at prefix[%s]: %s.", propertyPrefix, e.getMessage()), e);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new ProvisionException(StringUtils.format("Problem instantiating object at prefix[%s]: %s: %s.", propertyPrefix, e.getClass().getSimpleName(), e.getMessage()), e);
}
final Set<ConstraintViolation<T>> violations = validator.validate(config);
if (!violations.isEmpty()) {
List<String> messages = new ArrayList<>();
for (ConstraintViolation<T> violation : violations) {
StringBuilder path = new StringBuilder();
try {
Class<?> beanClazz = violation.getRootBeanClass();
final Iterator<Path.Node> iter = violation.getPropertyPath().iterator();
while (iter.hasNext()) {
Path.Node next = iter.next();
if (next.getKind() == ElementKind.PROPERTY) {
final String fieldName = next.getName();
final Field theField = beanClazz.getDeclaredField(fieldName);
if (theField.getAnnotation(JacksonInject.class) != null) {
path = new StringBuilder(StringUtils.format(" -- Injected field[%s] not bound!?", fieldName));
break;
}
JsonProperty annotation = theField.getAnnotation(JsonProperty.class);
final boolean noAnnotationValue = annotation == null || Strings.isNullOrEmpty(annotation.value());
final String pathPart = noAnnotationValue ? fieldName : annotation.value();
if (path.length() == 0) {
path.append(pathPart);
} else {
path.append(".").append(pathPart);
}
}
}
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
messages.add(StringUtils.format("%s - %s", path.toString(), violation.getMessage()));
}
throw new ProvisionException(Iterables.transform(messages, new Function<String, Message>() {
@Override
public Message apply(String input) {
return new Message(StringUtils.format("%s%s", propertyBase, input));
}
}));
}
log.debug("Loaded class[%s] from props[%s] as [%s]", clazz, propertyBase, config);
return config;
}
Aggregations