Search in sources :

Example 46 with Message

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);
        }
    };
}
Also used : Message(com.google.inject.spi.Message) Formatter(java.util.Formatter) ProvisionException(com.google.inject.ProvisionException) Provider(com.google.inject.Provider) ListMultimap(com.google.common.collect.ListMultimap) Key(com.google.inject.Key) Nullable(javax.annotation.Nullable)

Example 47 with Message

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);
}
Also used : TypeLiteral(com.google.inject.TypeLiteral) Message(com.google.inject.spi.Message) InjectionPoint(com.google.inject.spi.InjectionPoint)

Example 48 with Message

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;
}
Also used : Message(com.google.inject.spi.Message)

Example 49 with Message

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;
}
Also used : JsonProperty(com.fasterxml.jackson.annotation.JsonProperty) Message(com.google.inject.spi.Message) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) AnnotatedField(com.fasterxml.jackson.databind.introspect.AnnotatedField) Field(java.lang.reflect.Field) Function(com.google.common.base.Function) ProvisionException(com.google.inject.ProvisionException) JacksonInject(com.fasterxml.jackson.annotation.JacksonInject) Path(javax.validation.Path) IOException(java.io.IOException) InvocationTargetException(java.lang.reflect.InvocationTargetException) ConstraintViolation(javax.validation.ConstraintViolation)

Aggregations

Message (com.google.inject.spi.Message)49 ProvisionException (com.google.inject.ProvisionException)9 CreationException (com.google.inject.CreationException)7 ArrayList (java.util.ArrayList)7 AbstractModule (com.google.inject.AbstractModule)6 Provider (com.google.inject.Provider)5 TypeLiteral (com.google.inject.TypeLiteral)5 Injector (com.google.inject.Injector)4 Module (com.google.inject.Module)4 Errors (com.google.inject.internal.Errors)4 InjectionPoint (com.google.inject.spi.InjectionPoint)4 DisabledMetricMaker (com.google.gerrit.metrics.DisabledMetricMaker)3 GerritServerConfigModule (com.google.gerrit.server.config.GerritServerConfigModule)3 SitePath (com.google.gerrit.server.config.SitePath)3 Test (org.junit.Test)3 JacksonInject (com.fasterxml.jackson.annotation.JacksonInject)2 JsonProperty (com.fasterxml.jackson.annotation.JsonProperty)2 AnnotatedField (com.fasterxml.jackson.databind.introspect.AnnotatedField)2 Function (com.google.common.base.Function)2 ImmutableList (com.google.common.collect.ImmutableList)2